vim-patch:8.2.0869: it is not possible to customize the quickfix window contents

Problem:    It is not possible to customize the quickfix window contents.
Solution:   Add 'quickfixtextfunc'. (Yegappan Lakshmanan, closes vim/vim#5465)
858ba06d5f
This commit is contained in:
kevinhwang91 2021-05-04 00:05:45 +08:00
parent 52ca7f1a26
commit ebe1a08366
8 changed files with 354 additions and 71 deletions

View File

@ -4773,8 +4773,9 @@ getqflist([{what}]) *getqflist()*
id get information for the quickfix list with
|quickfix-ID|; zero means the id for the
current list or the list specified by "nr"
idx index of the current entry in the quickfix
list specified by 'id' or 'nr'.
idx get information for the quickfix entry at this
index in the list specified by 'id' or 'nr'.
If set to zero, then uses the current entry.
See |quickfix-index|
items quickfix list entries
lines parse a list of lines using 'efm' and return
@ -4807,7 +4808,7 @@ getqflist([{what}]) *getqflist()*
If not present, set to "".
id quickfix list ID |quickfix-ID|. If not
present, set to 0.
idx index of the current entry in the list. If not
idx index of the quickfix entry in the list. If not
present, set to 0.
items quickfix list entries. If not present, set to
an empty list.
@ -7874,6 +7875,11 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()*
nr list number in the quickfix stack; zero
means the current quickfix list and "$" means
the last quickfix list.
quickfixtextfunc
function to get the text to display in the
quickfix window. Refer to
|quickfix-window-function| for an explanation
of how to write the function and an example.
title quickfix list title text. See |quickfix-title|
Unsupported keys in {what} are ignored.
If the "nr" item is not present, then the current quickfix list

View File

@ -4600,6 +4600,19 @@ A jump table for the options with a short description can be found at |Q_op|.
'pyxversion' has no effect. The pyx* functions and commands are
always the same as the installed version.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
*'quickfixtextfunc'* *'qftf'*
'quickfixtextfunc' 'qftf' string (default "")
global
This option specifies a function to be used to get the text to display
in the quickfix and location list windows. This can be used to
customize the information displayed in the quickfix or location window
for each entry in the corresponding quickfix or location list. See
|quickfix-window-function| for an explanation of how to write the
function and an example.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.

View File

@ -1889,5 +1889,59 @@ be used. See the description further above how to make such a filter known
by Vim.
=============================================================================
10. Customizing the quickfix window *quickfix-window-function*
The default format for the lines displayed in the quickfix window and location
list window is:
<filename>|<lnum> col <col>|<text>
The values displayed in each line correspond to the "bufnr", "lnum", "col" and
"text" fields returned by the |getqflist()| function.
For some quickfix/location lists, the displayed text need to be customized.
For example, if only the filename is present for a quickfix entry, then the
two "|" field separator characters after the filename are not needed. Another
use case is to customize the path displayed for a filename. By default, the
complete path (which may be too long) is displayed for files which are not
under the current directory tree. The file path may need to be simplified to a
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:
quickfix set to 1 when called for a quickfix list and 0 when called for
a location list.
id quickfix or location list identifier
idx index of the entry in the quickfix or location list
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".
If a quickfix or location list specific customization is needed, then the
'quickfixtextfunc' attribute of the list can be set using the |setqflist()| or
|setloclist()| function. This overrides the global 'quickfixtextfunc' option.
The example below displays the list of old files (|v:oldfiles|) in a quickfix
window. As there is no line, column number and error text information
associated with each entry, the 'quickfixtextfunc' function returns only the
filename.
Example: >
" create a quickfix list from v:oldfiles
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:.')
endfunc
<
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -6326,7 +6326,7 @@ void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg,
if (what_arg->v_type == VAR_UNKNOWN) {
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (is_qf || wp != NULL) {
(void)get_errorlist(NULL, wp, -1, rettv->vval.v_list);
(void)get_errorlist(NULL, wp, -1, 0, rettv->vval.v_list);
}
} else {
tv_dict_alloc_ret(rettv);

View File

@ -559,6 +559,7 @@ EXTERN int p_ri; // 'revins'
EXTERN int p_ru; // 'ruler'
EXTERN char_u *p_ruf; // 'rulerformat'
EXTERN char_u *p_pp; // 'packpath'
EXTERN char_u *p_qftf; // 'quickfixtextfunc'
EXTERN char_u *p_rtp; // 'runtimepath'
EXTERN long p_scbk; // 'scrollback'
EXTERN long p_sj; // 'scrolljump'

View File

@ -2073,6 +2073,14 @@ return {
varname='p_pyx',
defaults={if_true={vi=0}}
},
{
full_name='quickfixtextfunc', abbreviation='qftf',
short_desc=N_("customize the quickfix window"),
type='string', scope={'global'},
vi_def=true,
varname='p_qftf',
defaults={if_true={vi=""}}
},
{
full_name='quoteescape', abbreviation='qe',
short_desc=N_("escape characters used in a string"),

View File

@ -100,6 +100,7 @@ typedef struct qf_list_S {
char_u *qf_title; ///< title derived from the command that created
///< the error list or set by setqflist
typval_T *qf_ctx; ///< context set by setqflist/setloclist
char_u *qf_qftf; ///< 'quickfixtextfunc' setting for this list
struct dir_stack_T *qf_dir_stack;
char_u *qf_directory;
@ -1999,6 +2000,11 @@ static int copy_loclist(const qf_list_T *from_qfl, qf_list_T *to_qfl)
} else {
to_qfl->qf_ctx = NULL;
}
if (from_qfl->qf_qftf != NULL) {
to_qfl->qf_qftf = vim_strsave(from_qfl->qf_qftf);
} else {
to_qfl->qf_qftf = NULL;
}
if (from_qfl->qf_count) {
if (copy_loclist_entries(from_qfl, to_qfl) == FAIL) {
@ -3382,6 +3388,7 @@ static void qf_free(qf_list_T *qfl)
XFREE_CLEAR(qfl->qf_title);
tv_free(qfl->qf_ctx);
qfl->qf_ctx = NULL;
XFREE_CLEAR(qfl->qf_qftf);
qfl->qf_id = 0;
qfl->qf_changedtick = 0L;
}
@ -3911,70 +3918,103 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
}
// Add an error line to the quickfix buffer.
static int qf_buf_add_line(buf_T *buf, linenr_T lnum, const qfline_T *qfp,
static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum,
const qfline_T *qfp,
char_u *dirname, bool first_bufline)
FUNC_ATTR_NONNULL_ALL
{
int len;
buf_T *errbuf;
char_u *qftf;
if (qfp->qf_module != NULL) {
STRLCPY(IObuff, qfp->qf_module, IOSIZE - 1);
len = (int)STRLEN(IObuff);
} else if (qfp->qf_fnum != 0
&& (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL
&& errbuf->b_fname != NULL) {
if (qfp->qf_type == 1) { // :helpgrep
STRLCPY(IObuff, path_tail(errbuf->b_fname), IOSIZE - 1);
} else {
// Shorten the file name if not done already.
// For optimization, do this only for the first entry in a
// buffer.
if (first_bufline
&& (errbuf->b_sfname == NULL
|| path_is_absolute(errbuf->b_sfname))) {
if (*dirname == NUL) {
os_dirname(dirname, MAXPATHL);
}
shorten_buf_fname(errbuf, dirname, false);
}
STRLCPY(IObuff, errbuf->b_fname, IOSIZE - 1);
// 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];
// create 'info' dict argument
dict_T *const dict = tv_dict_alloc_lock(VAR_FIXED);
tv_dict_add_nr(dict, S_LEN("quickfix"), IS_QF_LIST(qfl));
tv_dict_add_nr(dict, S_LEN("id"), qfl->qf_id);
tv_dict_add_nr(dict, S_LEN("idx"), lnum + 1);
dict->dv_refcount++;
args[0].v_type = VAR_DICT;
args[0].vval.v_dict = dict;
qfbuf_text = (char_u *)call_func_retstr((const char *const)qftf, 1, args);
dict->dv_refcount--;
if (qfbuf_text == NULL) {
return FAIL;
}
len = (int)STRLEN(IObuff);
STRLCPY(IObuff, qfbuf_text, IOSIZE - 1);
xfree(qfbuf_text);
} else {
len = 0;
}
if (len < IOSIZE - 1) {
IObuff[len++] = '|';
}
if (qfp->qf_lnum > 0) {
snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), "%" PRId64,
(int64_t)qfp->qf_lnum);
len += (int)STRLEN(IObuff + len);
if (qfp->qf_module != NULL) {
STRLCPY(IObuff, qfp->qf_module, IOSIZE - 1);
len = (int)STRLEN(IObuff);
} else if (qfp->qf_fnum != 0
&& (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL
&& errbuf->b_fname != NULL) {
if (qfp->qf_type == 1) { // :helpgrep
STRLCPY(IObuff, path_tail(errbuf->b_fname), IOSIZE - 1);
} else {
// Shorten the file name if not done already.
// For optimization, do this only for the first entry in a
// buffer.
if (first_bufline
&& (errbuf->b_sfname == NULL
|| path_is_absolute(errbuf->b_sfname))) {
if (*dirname == NUL) {
os_dirname(dirname, MAXPATHL);
}
shorten_buf_fname(errbuf, dirname, false);
}
STRLCPY(IObuff, errbuf->b_fname, IOSIZE - 1);
}
len = (int)STRLEN(IObuff);
} else {
len = 0;
}
if (len < IOSIZE - 1) {
IObuff[len++] = '|';
}
if (qfp->qf_lnum > 0) {
snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), "%" PRId64,
(int64_t)qfp->qf_lnum);
len += (int)STRLEN(IObuff + len);
if (qfp->qf_col > 0) {
snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), " col %d",
qfp->qf_col);
if (qfp->qf_col > 0) {
snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), " col %d",
qfp->qf_col);
len += (int)STRLEN(IObuff + len);
}
snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), "%s",
(char *)qf_types(qfp->qf_type, qfp->qf_nr));
len += (int)STRLEN(IObuff + len);
} else if (qfp->qf_pattern != NULL) {
qf_fmt_text(qfp->qf_pattern, IObuff + len, IOSIZE - len);
len += (int)STRLEN(IObuff + len);
}
if (len < IOSIZE - 2) {
IObuff[len++] = '|';
IObuff[len++] = ' ';
}
snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), "%s",
(char *)qf_types(qfp->qf_type, qfp->qf_nr));
len += (int)STRLEN(IObuff + len);
} else if (qfp->qf_pattern != NULL) {
qf_fmt_text(qfp->qf_pattern, IObuff + len, IOSIZE - len);
len += (int)STRLEN(IObuff + len);
// Remove newlines and leading whitespace from the text.
// For an unrecognized line keep the indent, the compiler may
// mark a word with ^^^^.
qf_fmt_text(len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text,
IObuff + len, IOSIZE - len);
}
if (len < IOSIZE - 2) {
IObuff[len++] = '|';
IObuff[len++] = ' ';
}
// Remove newlines and leading whitespace from the text.
// For an unrecognized line keep the indent, the compiler may
// mark a word with ^^^^.
qf_fmt_text(len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text,
IObuff + len, IOSIZE - len);
if (ml_append_buf(buf, lnum, IObuff,
(colnr_T)STRLEN(IObuff) + 1, false) == FAIL) {
@ -4027,7 +4067,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last)
lnum = buf->b_ml.ml_line_count;
}
while (lnum < qfl->qf_count) {
if (qf_buf_add_line(buf, lnum, qfp, dirname,
if (qf_buf_add_line(qfl, buf, lnum, qfp, dirname,
prev_bufnr != qfp->qf_fnum) == FAIL) {
break;
}
@ -5665,7 +5705,10 @@ static int get_qfline_items(qfline_T *qfp, list_T *list)
/// Add each quickfix error to list "list" as a dictionary.
/// If qf_idx is -1, use the current list. Otherwise, use the specified list.
int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, list_T *list)
/// If eidx is not 0, then return only the specified entry. Otherwise return
/// all the entries.
int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, int eidx,
list_T *list)
{
qf_info_T *qi = qi_arg;
qf_list_T *qfl;
@ -5682,6 +5725,10 @@ int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, list_T *list)
}
}
if (eidx < 0) {
return OK;
}
if (qf_idx == INVALID_QFIDX) {
qf_idx = qi->qf_curlist;
}
@ -5696,7 +5743,13 @@ int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, list_T *list)
}
FOR_ALL_QFL_ITEMS(qfl, qfp, i) {
get_qfline_items(qfp, list);
if (eidx > 0) {
if (eidx == i) {
return get_qfline_items(qfp, list);
}
} else if (get_qfline_items(qfp, list) == FAIL) {
return FAIL;
}
}
return OK;
@ -5743,7 +5796,7 @@ static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict)
if (qf_init_ext(qi, 0, NULL, NULL, &di->di_tv, errorformat,
true, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) {
(void)get_errorlist(qi, NULL, 0, l);
(void)get_errorlist(qi, NULL, 0, 0, l);
qf_free(&qi->qf_lists[0]);
}
xfree(qi);
@ -5934,11 +5987,13 @@ static int qf_getprop_filewinid(const win_T *wp, const qf_info_T *qi,
return tv_dict_add_nr(retdict, S_LEN("filewinid"), winid);
}
/// Return the quickfix list items/entries as 'items' in retdict
static int qf_getprop_items(qf_info_T *qi, int qf_idx, dict_T *retdict)
/// Return the quickfix list items/entries as 'items' in retdict.
/// If eidx is not 0, then return the item at the specified index.
static int qf_getprop_items(qf_info_T *qi, int qf_idx, int eidx,
dict_T *retdict)
{
list_T *l = tv_list_alloc(kListLenMayKnow);
get_errorlist(qi, NULL, qf_idx, l);
get_errorlist(qi, NULL, qf_idx, eidx, l);
tv_dict_add_list(retdict, S_LEN("items"), l);
return OK;
@ -5963,15 +6018,18 @@ static int qf_getprop_ctx(qf_list_T *qfl, dict_T *retdict)
return status;
}
/// Return the current quickfix list index as 'idx' in retdict
static int qf_getprop_idx(qf_list_T *qfl, dict_T *retdict)
/// Return the current quickfix list index as 'idx' in retdict.
/// If a specific entry index (eidx) is supplied, then use that.
static int qf_getprop_idx(qf_list_T *qfl, int eidx, dict_T *retdict)
{
int curidx = qfl->qf_index;
if (qf_list_empty(qfl)) {
// For empty lists, current index is set to 0
curidx = 0;
if (eidx == 0) {
eidx = qfl->qf_index;
if (qf_list_empty(qfl)) {
// For empty lists, current index is set to 0
eidx = 0;
}
}
return tv_dict_add_nr(retdict, S_LEN("idx"), curidx);
return tv_dict_add_nr(retdict, S_LEN("idx"), eidx);
}
/// Return quickfix/location list details (title) as a dictionary.
@ -5984,6 +6042,7 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
dictitem_T *di = NULL;
int status = OK;
int qf_idx = INVALID_QFIDX;
int eidx = 0;
if ((di = tv_dict_find(what, S_LEN("lines"))) != NULL) {
return qf_get_list_from_lines(what, di, retdict);
@ -6006,6 +6065,14 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
qfl = qf_get_list(qi, qf_idx);
// If an entry index is specified, use that
if ((di = tv_dict_find(what, S_LEN("idx"))) != NULL) {
if (di->di_tv.v_type != VAR_NUMBER) {
return FAIL;
}
eidx = (int)di->di_tv.vval.v_number;
}
if (flags & QF_GETLIST_TITLE) {
status = qf_getprop_title(qfl, retdict);
}
@ -6016,7 +6083,7 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
status = tv_dict_add_nr(retdict, S_LEN("winid"), qf_winid(qi));
}
if ((status == OK) && (flags & QF_GETLIST_ITEMS)) {
status = qf_getprop_items(qi, qf_idx, retdict);
status = qf_getprop_items(qi, qf_idx, eidx, retdict);
}
if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) {
status = qf_getprop_ctx(qfl, retdict);
@ -6025,7 +6092,7 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
status = tv_dict_add_nr(retdict, S_LEN("id"), qfl->qf_id);
}
if ((status == OK) && (flags & QF_GETLIST_IDX)) {
status = qf_getprop_idx(qfl, retdict);
status = qf_getprop_idx(qfl, eidx, retdict);
}
if ((status == OK) && (flags & QF_GETLIST_SIZE)) {
status = tv_dict_add_nr(retdict, S_LEN("size"),
@ -6042,6 +6109,17 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
return status;
}
/// Set the current index in the specified quickfix list
static int qf_setprop_qftf(qf_info_T *qi, qf_list_T *qfl,
dictitem_T *di)
{
XFREE_CLEAR(qfl->qf_qftf);
if (di->di_tv.v_type == VAR_STRING && di->di_tv.vval.v_string != NULL) {
qfl->qf_qftf = vim_strsave(di->di_tv.vval.v_string);
}
return OK;
}
/// Add a new quickfix entry to list at 'qf_idx' in the stack 'qi' from the
/// items in the dict 'd'. If it is a valid error entry, then set 'valid_entry'
/// to true.
@ -6407,6 +6485,9 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action,
if ((di = tv_dict_find(what, S_LEN("idx"))) != NULL) {
retval = qf_setprop_curidx(qi, qfl, di);
}
if ((di = tv_dict_find(what, S_LEN("quickfixtextfunc"))) != NULL) {
retval = qf_setprop_qftf(qi, qfl, di);
}
if (newlist || retval == OK) {
qf_list_changed(qfl);

View File

@ -4786,4 +4786,124 @@ func Test_qfbuf_update()
call Xqfbuf_update('l')
endfunc
" Test for getting a specific item from a quickfix list
func Xtest_getqflist_by_idx(cchar)
call s:setup_commands(a:cchar)
" Empty list
call assert_equal([], g:Xgetlist({'idx' : 1, 'items' : 0}).items)
Xexpr ['F1:10:L10', 'F1:20:L20']
let l = g:Xgetlist({'idx' : 2, 'items' : 0}).items
call assert_equal(bufnr('F1'), l[0].bufnr)
call assert_equal(20, l[0].lnum)
call assert_equal('L20', l[0].text)
call assert_equal([], g:Xgetlist({'idx' : -1, 'items' : 0}).items)
call assert_equal([], g:Xgetlist({'idx' : 3, 'items' : 0}).items)
%bwipe!
endfunc
func Test_getqflist_by_idx()
call Xtest_getqflist_by_idx('c')
call Xtest_getqflist_by_idx('l')
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
else
let qfl = getloclist(0, {'id' : a:info.id, 'idx' : a:info.idx,
\ '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
endfunc
func Xtest_qftextfunc(cchar)
call s:setup_commands(a:cchar)
set efm=%f:%l:%c:%m
set quickfixtextfunc=Tqfexpr
Xexpr ['F1:10:2:green', 'F1:20:4:blue']
Xwindow
call assert_equal('F1-L10C2-green', getline(1))
call assert_equal('F1-L20C4-blue', getline(2))
Xclose
set quickfixtextfunc&vim
Xwindow
call assert_equal('F1|10 col 2| green', getline(1))
call assert_equal('F1|20 col 4| blue', getline(2))
Xclose
set efm&
set quickfixtextfunc&
" 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
else
let qfl = getloclist(0, {'id' : a:info.id, 'idx' : a:info.idx,
\ 'items' : 1}).items
endif
if empty(qfl)
return ''
endif
return 'Line ' .. qfl[0].lnum .. ', Col ' .. qfl[0].col
endfunc
set quickfixtextfunc=Tqfexpr
call g:Xsetlist([], ' ', {'quickfixtextfunc' : "PerQfText"})
Xaddexpr ['F1:10:2:green', 'F1:20:4:blue']
Xwindow
call assert_equal('Line 10, Col 2', getline(1))
call assert_equal('Line 20, Col 4', getline(2))
Xclose
call g:Xsetlist([], 'r', {'quickfixtextfunc' : ''})
set quickfixtextfunc&
delfunc PerQfText
" Non-existing function
set quickfixtextfunc=Tabc
" call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E117:')
Xexpr ['F1:10:2:green', 'F1:20:4:blue']"
call assert_fails("Xwindow", 'E117:')
Xclose
set quickfixtextfunc&
" set option to a non-function
set quickfixtextfunc=[10,\ 20]
" call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E117:')
Xexpr ['F1:10:2:green', 'F1:20:4:blue']"
call assert_fails("Xwindow", 'E117:')
Xclose
set quickfixtextfunc&
" set option to a function with different set of arguments
func Xqftext(a, b, c)
return a:a .. a:b .. a:c
endfunc
set quickfixtextfunc=Xqftext
" call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E119:')
Xexpr ['F1:10:2:green', 'F1:20:4:blue']"
call assert_fails("Xwindow", 'E119:')
Xclose
set quickfixtextfunc&
delfunc Xqftext
endfunc
func Test_qftextfunc()
call Xtest_qftextfunc('c')
call Xtest_qftextfunc('l')
endfunc
" vim: shiftwidth=2 sts=2 expandtab