vim-patch:8.2.1255: cannot use a lambda with quickfix functions

Problem:    Cannot use a lambda with quickfix functions.
Solution:   Add support for lambda. (Yegappan Lakshmanan, closes vim/vim#6499)
d43906d2e5
This commit is contained in:
Jan Edmund Lazo 2021-06-22 22:02:59 -04:00
parent d5329c0331
commit 4cb0bf0942
No known key found for this signature in database
GPG Key ID: 64915E6E9F735B15
9 changed files with 232 additions and 51 deletions

View File

@ -7879,7 +7879,8 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()*
the last quickfix list. the last quickfix list.
quickfixtextfunc quickfixtextfunc
function to get the text to display in the function to get the text to display in the
quickfix window. Refer to quickfix window. The value can be the name of
a function or a funcref or a lambda. Refer to
|quickfix-window-function| for an explanation |quickfix-window-function| for an explanation
of how to write the function and an example. of how to write the function and an example.
title quickfix list title text. See |quickfix-title| title quickfix list title text. See |quickfix-title|

View File

@ -4607,7 +4607,8 @@ A jump table for the options with a short description can be found at |Q_op|.
customize the information displayed in the quickfix or location window customize the information displayed in the quickfix or location window
for each entry in the corresponding quickfix or location list. See for each entry in the corresponding quickfix or location list. See
|quickfix-window-function| for an explanation of how to write the |quickfix-window-function| for an explanation of how to write the
function and an example. function and an example. The value can be the name of a function or a
lambda.
This option cannot be set from a |modeline| or in the |sandbox|, for This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons. security reasons.

View File

@ -1919,7 +1919,10 @@ The function should return a single line of text to display in the quickfix
window for each entry from start_idx to end_idx. The function can obtain window for each entry from start_idx to end_idx. The function can obtain
information about the entries using the |getqflist()| function and specifying information about the entries using the |getqflist()| function and specifying
the quickfix list identifier "id". For a location list, getloclist() function the quickfix list identifier "id". For a location list, getloclist() function
can be used with the 'winid' argument. can be used with the 'winid' argument. If an empty list is returned, then the
default format is used to display all the entries. If an item in the returned
list is an empty string, then the default format is used to display the
corresponding entry.
If a quickfix or location list specific customization is needed, then the If a quickfix or location list specific customization is needed, then the
'quickfixtextfunc' attribute of the list can be set using the |setqflist()| or 'quickfixtextfunc' attribute of the list can be set using the |setqflist()| or

View File

@ -7200,12 +7200,15 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg)
r = FAIL; r = FAIL;
} else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) { } else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) {
char_u *name = arg->vval.v_string; char_u *name = arg->vval.v_string;
if (name != NULL) { if (name == NULL) {
r = FAIL;
} else if (*name == NUL) {
callback->type = kCallbackNone;
callback->data.funcref = NULL;
} else {
func_ref(name); func_ref(name);
callback->data.funcref = vim_strsave(name); callback->data.funcref = vim_strsave(name);
callback->type = kCallbackFuncref; callback->type = kCallbackFuncref;
} else {
r = FAIL;
} }
} else if (nlua_is_table_from_lua(arg)) { } else if (nlua_is_table_from_lua(arg)) {
char_u *name = nlua_register_table_as_callable(arg); char_u *name = nlua_register_table_as_callable(arg);
@ -7216,8 +7219,10 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg)
} else { } else {
r = FAIL; r = FAIL;
} }
} else if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) { } else if (arg->v_type == VAR_SPECIAL
|| (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)) {
callback->type = kCallbackNone; callback->type = kCallbackNone;
callback->data.funcref = NULL;
} else { } else {
r = FAIL; r = FAIL;
} }

View File

@ -1162,20 +1162,48 @@ void callback_free(Callback *callback)
} }
} }
callback->type = kCallbackNone; callback->type = kCallbackNone;
callback->data.funcref = NULL;
} }
/// Copy a callback into a typval_T. /// Copy a callback into a typval_T.
void callback_put(Callback *cb, typval_T *tv) void callback_put(Callback *cb, typval_T *tv)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
if (cb->type == kCallbackPartial) { switch (cb->type) {
tv->v_type = VAR_PARTIAL; case kCallbackPartial:
tv->vval.v_partial = cb->data.partial; tv->v_type = VAR_PARTIAL;
cb->data.partial->pt_refcount++; tv->vval.v_partial = cb->data.partial;
} else if (cb->type == kCallbackFuncref) { cb->data.partial->pt_refcount++;
tv->v_type = VAR_FUNC; break;
tv->vval.v_string = vim_strsave(cb->data.funcref); case kCallbackFuncref:
func_ref(cb->data.funcref); tv->v_type = VAR_FUNC;
tv->vval.v_string = vim_strsave(cb->data.funcref);
func_ref(cb->data.funcref);
break;
default:
tv->v_type = VAR_SPECIAL;
tv->vval.v_special = kSpecialVarNull;
break;
}
}
// Copy callback from "src" to "dest", incrementing the refcounts.
void callback_copy(Callback *dest, Callback *src)
FUNC_ATTR_NONNULL_ALL
{
dest->type = src->type;
switch (src->type) {
case kCallbackPartial:
dest->data.partial = src->data.partial;
dest->data.partial->pt_refcount++;
break;
case kCallbackFuncref:
dest->data.funcref = vim_strsave(src->data.funcref);
func_ref(src->data.funcref);
break;
default:
dest->data.funcref = NULL;
break;
} }
} }

View File

@ -120,7 +120,7 @@ typedef enum {
VAR_DICT, ///< Dictionary, .v_dict is used. VAR_DICT, ///< Dictionary, .v_dict is used.
VAR_FLOAT, ///< Floating-point value, .v_float is used. VAR_FLOAT, ///< Floating-point value, .v_float is used.
VAR_BOOL, ///< true, false VAR_BOOL, ///< true, false
VAR_SPECIAL, ///< Special value (true, false, null), .v_special VAR_SPECIAL, ///< Special value (null), .v_special
///< is used. ///< is used.
VAR_PARTIAL, ///< Partial, .v_partial is used. VAR_PARTIAL, ///< Partial, .v_partial is used.
} VarType; } VarType;

View File

@ -85,6 +85,7 @@
#include "nvim/api/private/helpers.h" #include "nvim/api/private/helpers.h"
#include "nvim/os/input.h" #include "nvim/os/input.h"
#include "nvim/os/lang.h" #include "nvim/os/lang.h"
#include "nvim/quickfix.h"
/* /*
* The options that are local to a window or buffer have "indir" set to one of * The options that are local to a window or buffer have "indir" set to one of
@ -3182,6 +3183,10 @@ ambw_end:
} }
} }
} }
} else if (varp == &p_qftf) {
if (!qf_process_qftf_option()) {
errmsg = e_invarg;
}
} else { } else {
// Options that are a list of flags. // Options that are a list of flags.
p = NULL; p = NULL;

View File

@ -100,7 +100,7 @@ typedef struct qf_list_S {
char_u *qf_title; ///< title derived from the command that created char_u *qf_title; ///< title derived from the command that created
///< the error list or set by setqflist ///< the error list or set by setqflist
typval_T *qf_ctx; ///< context set by setqflist/setloclist typval_T *qf_ctx; ///< context set by setqflist/setloclist
char_u *qf_qftf; ///< 'quickfixtextfunc' setting for this list Callback qftf_cb; ///< 'quickfixtextfunc' callback function
struct dir_stack_T *qf_dir_stack; struct dir_stack_T *qf_dir_stack;
char_u *qf_directory; char_u *qf_directory;
@ -541,6 +541,9 @@ static int efm_to_regpat(const char_u *efm, int len, efm_T *fmt_ptr,
static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls
// callback function for 'quickfixtextfunc'
static Callback qftf_cb;
static void free_efm_list(efm_T **efm_first) static void free_efm_list(efm_T **efm_first)
{ {
for (efm_T *efm_ptr = *efm_first; efm_ptr != NULL; efm_ptr = *efm_first) { for (efm_T *efm_ptr = *efm_first; efm_ptr != NULL; efm_ptr = *efm_first) {
@ -1978,7 +1981,7 @@ static int copy_loclist_entries(const qf_list_T *from_qfl, qf_list_T *to_qfl)
} }
/// Copy the specified location list 'from_qfl' to 'to_qfl'. /// Copy the specified location list 'from_qfl' to 'to_qfl'.
static int copy_loclist(const qf_list_T *from_qfl, qf_list_T *to_qfl) static int copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
// Some of the fields are populated by qf_add_entry() // Some of the fields are populated by qf_add_entry()
@ -2000,11 +2003,7 @@ static int copy_loclist(const qf_list_T *from_qfl, qf_list_T *to_qfl)
} else { } else {
to_qfl->qf_ctx = NULL; to_qfl->qf_ctx = NULL;
} }
if (from_qfl->qf_qftf != NULL) { callback_copy(&to_qfl->qftf_cb, &from_qfl->qftf_cb);
to_qfl->qf_qftf = vim_strsave(from_qfl->qf_qftf);
} else {
to_qfl->qf_qftf = NULL;
}
if (from_qfl->qf_count) { if (from_qfl->qf_count) {
if (copy_loclist_entries(from_qfl, to_qfl) == FAIL) { if (copy_loclist_entries(from_qfl, to_qfl) == FAIL) {
@ -3385,7 +3384,7 @@ static void qf_free(qf_list_T *qfl)
XFREE_CLEAR(qfl->qf_title); XFREE_CLEAR(qfl->qf_title);
tv_free(qfl->qf_ctx); tv_free(qfl->qf_ctx);
qfl->qf_ctx = NULL; qfl->qf_ctx = NULL;
XFREE_CLEAR(qfl->qf_qftf); callback_free(&qfl->qftf_cb);
qfl->qf_id = 0; qfl->qf_id = 0;
qfl->qf_changedtick = 0L; qfl->qf_changedtick = 0L;
} }
@ -3860,6 +3859,41 @@ static buf_T *qf_find_buf(qf_info_T *qi)
return NULL; return NULL;
} }
// Process the 'quickfixtextfunc' option value.
bool qf_process_qftf_option(void)
{
typval_T *tv;
Callback cb;
if (p_qftf == NULL || *p_qftf == NUL) {
callback_free(&qftf_cb);
return true;
}
if (*p_qftf == '{') {
// Lambda expression
tv = eval_expr(p_qftf);
if (tv == NULL) {
return false;
}
} else {
// treat everything else as a function name string
tv = xcalloc(1, sizeof(*tv));
tv->v_type = VAR_STRING;
tv->vval.v_string = vim_strsave(p_qftf);
}
if (!callback_from_typval(&cb, tv)) {
tv_free(tv);
return false;
}
callback_free(&qftf_cb);
qftf_cb = cb;
tv_free(tv);
return true;
}
/// Update the w:quickfix_title variable in the quickfix/location list window in /// Update the w:quickfix_title variable in the quickfix/location list window in
/// all the tab pages. /// all the tab pages.
static void qf_update_win_titlevar(qf_info_T *qi) static void qf_update_win_titlevar(qf_info_T *qi)
@ -3928,7 +3962,9 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum,
int len; int len;
buf_T *errbuf; buf_T *errbuf;
if (qftf_str != NULL) { // If the 'quickfixtextfunc' function returned an non-empty custom string
// for this entry, then use it.
if (qftf_str != NULL && *qftf_str != NUL) {
STRLCPY(IObuff, qftf_str, IOSIZE); STRLCPY(IObuff, qftf_str, IOSIZE);
} else { } else {
if (qfp->qf_module != NULL) { if (qfp->qf_module != NULL) {
@ -3997,22 +4033,25 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum,
return OK; return OK;
} }
// Call the 'quickfixtextfunc' function to get the list of lines to display in
// the quickfix window for the entries 'start_idx' to 'end_idx'.
static list_T *call_qftf_func(qf_list_T *qfl, static list_T *call_qftf_func(qf_list_T *qfl,
int qf_winid, int qf_winid,
long start_idx, long start_idx,
long end_idx) long end_idx)
{ {
char_u *qftf = p_qftf; Callback *cb = &qftf_cb;
list_T *qftf_list = NULL; list_T *qftf_list = NULL;
// If 'quickfixtextfunc' is set, then use the user-supplied function to get // 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 // the text to display. Use the local value of 'quickfixtextfunc' if it is
// set. // set.
if (qfl->qf_qftf != NULL) { if (qfl->qftf_cb.type != kCallbackNone) {
qftf = qfl->qf_qftf; cb = &qfl->qftf_cb;
} }
if (qftf != NULL && *qftf != NUL) { if (cb != NULL && cb->type != kCallbackNone) {
typval_T args[1]; typval_T args[1];
typval_T rettv;
// create the dict argument // create the dict argument
dict_T *const dict = tv_dict_alloc_lock(VAR_FIXED); dict_T *const dict = tv_dict_alloc_lock(VAR_FIXED);
@ -4026,8 +4065,16 @@ static list_T *call_qftf_func(qf_list_T *qfl,
args[0].v_type = VAR_DICT; args[0].v_type = VAR_DICT;
args[0].vval.v_dict = dict; args[0].vval.v_dict = dict;
qftf_list = call_func_retlist(qftf, 1, args); qftf_list = NULL;
dict->dv_refcount--;
if (callback_call(cb, 1, args, &rettv)) {
if (rettv.v_type == VAR_LIST) {
qftf_list = rettv.vval.v_list;
tv_list_ref(qftf_list);
}
tv_clear(&rettv);
}
tv_dict_unref(dict);
} }
return qftf_list; return qftf_list;
@ -4064,6 +4111,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last,
if (qfl != NULL) { if (qfl != NULL) {
char_u dirname[MAXPATHL]; char_u dirname[MAXPATHL];
int prev_bufnr = -1; int prev_bufnr = -1;
bool invalid_val = false;
*dirname = NUL; *dirname = NUL;
@ -4086,10 +4134,15 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last,
while (lnum < qfl->qf_count) { while (lnum < qfl->qf_count) {
char_u *qftf_str = NULL; char_u *qftf_str = NULL;
if (qftf_li != NULL) { // Use the text supplied by the user defined function (if any).
// Use the text supplied by the user defined function // If the returned value is not string, then ignore the rest
qftf_str = (char_u *)tv_get_string_chk(TV_LIST_ITEM_TV(qftf_li)); // of the returned values and use the default.
if (qftf_li != NULL && !invalid_val) {
qftf_str = (char_u *)tv_get_string_chk(TV_LIST_ITEM_TV(qftf_li));
if (qftf_str == NULL) {
invalid_val = true;
} }
}
if (qf_buf_add_line(qfl, buf, lnum, qfp, dirname, qftf_str, if (qf_buf_add_line(qfl, buf, lnum, qfp, dirname, qftf_str,
prev_bufnr != qfp->qf_fnum) == FAIL) { prev_bufnr != qfp->qf_fnum) == FAIL) {
@ -5796,7 +5849,9 @@ enum {
QF_GETLIST_SIZE = 0x80, QF_GETLIST_SIZE = 0x80,
QF_GETLIST_TICK = 0x100, QF_GETLIST_TICK = 0x100,
QF_GETLIST_FILEWINID = 0x200, QF_GETLIST_FILEWINID = 0x200,
QF_GETLIST_ALL = 0x3FF, QF_GETLIST_QFBUFNR = 0x400,
QF_GETLIST_QFTF = 0x800,
QF_GETLIST_ALL = 0xFFF,
}; };
/// Parse text from 'di' and return the quickfix list items. /// Parse text from 'di' and return the quickfix list items.
@ -5894,6 +5949,9 @@ static int qf_getprop_keys2flags(const dict_T *what, bool loclist)
if (loclist && tv_dict_find(what, S_LEN("filewinid")) != NULL) { if (loclist && tv_dict_find(what, S_LEN("filewinid")) != NULL) {
flags |= QF_GETLIST_FILEWINID; flags |= QF_GETLIST_FILEWINID;
} }
if (tv_dict_find(what, S_LEN("quickfixtextfunc")) != NULL) {
flags |= QF_GETLIST_QFTF;
}
return flags; return flags;
} }
@ -5985,6 +6043,9 @@ static int qf_getprop_defaults(qf_info_T *qi,
if ((status == OK) && locstack && (flags & QF_GETLIST_FILEWINID)) { if ((status == OK) && locstack && (flags & QF_GETLIST_FILEWINID)) {
status = tv_dict_add_nr(retdict, S_LEN("filewinid"), 0); status = tv_dict_add_nr(retdict, S_LEN("filewinid"), 0);
} }
if ((status == OK) && (flags & QF_GETLIST_QFTF)) {
status = tv_dict_add_str(retdict, S_LEN("quickfixtextfunc"), "");
}
return status; return status;
} }
@ -6060,6 +6121,26 @@ static int qf_getprop_idx(qf_list_T *qfl, int eidx, dict_T *retdict)
return tv_dict_add_nr(retdict, S_LEN("idx"), eidx); return tv_dict_add_nr(retdict, S_LEN("idx"), eidx);
} }
/// Return the 'quickfixtextfunc' function of a quickfix/location list
/// @return OK or FAIL
static int qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict)
FUNC_ATTR_NONNULL_ALL
{
int status;
if (qfl->qftf_cb.type != kCallbackNone) {
typval_T tv;
callback_put(&qfl->qftf_cb, &tv);
status = tv_dict_add_tv(retdict, S_LEN("quickfixtextfunc"), &tv);
tv_clear(&tv);
} else {
status = tv_dict_add_str(retdict, S_LEN("quickfixtextfunc"), "");
}
return status;
}
/// Return quickfix/location list details (title) as a dictionary. /// Return quickfix/location list details (title) as a dictionary.
/// 'what' contains the details to return. If 'list_idx' is -1, /// 'what' contains the details to return. If 'list_idx' is -1,
/// then current list is used. Otherwise the specified list is used. /// then current list is used. Otherwise the specified list is used.
@ -6133,19 +6214,25 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
if ((status == OK) && (wp != NULL) && (flags & QF_GETLIST_FILEWINID)) { if ((status == OK) && (wp != NULL) && (flags & QF_GETLIST_FILEWINID)) {
status = qf_getprop_filewinid(wp, qi, retdict); status = qf_getprop_filewinid(wp, qi, retdict);
} }
if ((status == OK) && (flags & QF_GETLIST_QFTF)) {
status = qf_getprop_qftf(qfl, retdict);
}
return status; return status;
} }
/// Set the current index in the specified quickfix list /// Set the current index in the specified quickfix list
static int qf_setprop_qftf(qf_info_T *qi, qf_list_T *qfl, /// @return OK
dictitem_T *di) static int qf_setprop_qftf(qf_list_T *qfl, dictitem_T *di)
FUNC_ATTR_NONNULL_ALL
{ {
XFREE_CLEAR(qfl->qf_qftf); Callback cb;
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); callback_free(&qfl->qftf_cb);
} if (callback_from_typval(&cb, &di->di_tv)) {
return OK; qfl->qftf_cb = cb;
}
return OK;
} }
/// Add a new quickfix entry to list at 'qf_idx' in the stack 'qi' from the /// Add a new quickfix entry to list at 'qf_idx' in the stack 'qi' from the
@ -6514,7 +6601,7 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action,
retval = qf_setprop_curidx(qi, qfl, di); retval = qf_setprop_curidx(qi, qfl, di);
} }
if ((di = tv_dict_find(what, S_LEN("quickfixtextfunc"))) != NULL) { if ((di = tv_dict_find(what, S_LEN("quickfixtextfunc"))) != NULL) {
retval = qf_setprop_qftf(qi, qfl, di); retval = qf_setprop_qftf(qfl, di);
} }
if (newlist || retval == OK) { if (newlist || retval == OK) {

View File

@ -3483,12 +3483,13 @@ func Xgetlist_empty_tests(cchar)
if a:cchar == 'c' if a:cchar == 'c'
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, call assert_equal({'context' : '', 'id' : 0, 'idx' : 0,
\ 'items' : [], 'nr' : 0, 'size' : 0, \ 'items' : [], 'nr' : 0, 'size' : 0,
\ 'title' : '', 'winid' : 0, 'changedtick': 0}, \ 'title' : '', 'winid' : 0, 'changedtick': 0,
\ g:Xgetlist({'all' : 0})) \ 'quickfixtextfunc' : ''}, g:Xgetlist({'all' : 0}))
else else
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, call assert_equal({'context' : '', 'id' : 0, 'idx' : 0,
\ 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', \ 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '',
\ 'winid' : 0, 'changedtick': 0, 'filewinid' : 0}, \ 'winid' : 0, 'changedtick': 0, 'filewinid' : 0,
\ 'quickfixtextfunc' : ''},
\ g:Xgetlist({'all' : 0})) \ g:Xgetlist({'all' : 0}))
endif endif
@ -3526,11 +3527,13 @@ func Xgetlist_empty_tests(cchar)
if a:cchar == 'c' if a:cchar == 'c'
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, \ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
\ 'quickfixtextfunc' : '',
\ 'changedtick' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0})) \ 'changedtick' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0}))
else else
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, \ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
\ 'changedtick' : 0, 'filewinid' : 0}, \ 'changedtick' : 0, 'filewinid' : 0,
\ 'quickfixtextfunc' : ''},
\ g:Xgetlist({'id' : qfid, 'all' : 0})) \ g:Xgetlist({'id' : qfid, 'all' : 0}))
endif endif
@ -3547,12 +3550,13 @@ func Xgetlist_empty_tests(cchar)
if a:cchar == 'c' if a:cchar == 'c'
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, \ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
\ 'changedtick' : 0}, g:Xgetlist({'nr' : 5, 'all' : 0})) \ 'changedtick' : 0,
\ 'quickfixtextfunc' : ''}, g:Xgetlist({'nr' : 5, 'all' : 0}))
else else
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, \ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
\ 'changedtick' : 0, 'filewinid' : 0}, \ 'changedtick' : 0, 'filewinid' : 0,
\ g:Xgetlist({'nr' : 5, 'all' : 0})) \ 'quickfixtextfunc' : ''}, g:Xgetlist({'nr' : 5, 'all' : 0}))
endif endif
endfunc endfunc
@ -4994,6 +4998,9 @@ func Xtest_qftextfunc(cchar)
set efm=%f:%l:%c:%m set efm=%f:%l:%c:%m
set quickfixtextfunc=Tqfexpr set quickfixtextfunc=Tqfexpr
call assert_equal('Tqfexpr', &quickfixtextfunc)
call assert_equal('',
\ g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc)
Xexpr ['F1:10:2:green', 'F1:20:4:blue'] Xexpr ['F1:10:2:green', 'F1:20:4:blue']
Xwindow Xwindow
call assert_equal('F1-L10C2-green', getline(1)) call assert_equal('F1-L10C2-green', getline(1))
@ -5030,12 +5037,15 @@ func Xtest_qftextfunc(cchar)
call assert_equal('Line 10, Col 2', getline(1)) call assert_equal('Line 10, Col 2', getline(1))
call assert_equal('Line 20, Col 4', getline(2)) call assert_equal('Line 20, Col 4', getline(2))
Xclose Xclose
call assert_equal(function('PerQfText'),
\ g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc)
" Add entries to the list when the quickfix buffer is hidden " Add entries to the list when the quickfix buffer is hidden
Xaddexpr ['F1:30:6:red'] Xaddexpr ['F1:30:6:red']
Xwindow Xwindow
call assert_equal('Line 30, Col 6', getline(3)) call assert_equal('Line 30, Col 6', getline(3))
Xclose Xclose
call g:Xsetlist([], 'r', {'quickfixtextfunc' : ''}) call g:Xsetlist([], 'r', {'quickfixtextfunc' : ''})
call assert_equal('', g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc)
set quickfixtextfunc& set quickfixtextfunc&
delfunc PerQfText delfunc PerQfText
@ -5074,12 +5084,53 @@ func Xtest_qftextfunc(cchar)
" \ 'E730:') " \ 'E730:')
Xexpr ['F1:10:2:green', 'F1:20:4:blue', 'F1:30:6:red'] Xexpr ['F1:10:2:green', 'F1:20:4:blue', 'F1:30:6:red']
call assert_fails('Xwindow', 'E730:') call assert_fails('Xwindow', 'E730:')
call assert_equal(['one', 'F1|20 col 4| blue', 'two'], getline(1, '$')) call assert_equal(['one', 'F1|20 col 4| blue', 'F1|30 col 6| red'],
\ getline(1, '$'))
Xclose Xclose
set quickfixtextfunc& set quickfixtextfunc&
delfunc Xqftext delfunc Xqftext
delfunc Xqftext2 delfunc Xqftext2
" set the global option to a lambda function
set quickfixtextfunc={d\ ->\ map(g:Xgetlist({'id'\ :\ d.id,\ 'items'\ :\ 1}).items[d.start_idx-1:d.end_idx-1],\ 'v:val.text')}
Xexpr ['F1:10:2:green', 'F1:20:4:blue']
Xwindow
call assert_equal(['green', 'blue'], getline(1, '$'))
Xclose
call assert_equal("{d -> map(g:Xgetlist({'id' : d.id, 'items' : 1}).items[d.start_idx-1:d.end_idx-1], 'v:val.text')}", &quickfixtextfunc)
set quickfixtextfunc&
" use a lambda function that returns an empty list
set quickfixtextfunc={d\ ->\ []}
Xexpr ['F1:10:2:green', 'F1:20:4:blue']
Xwindow
call assert_equal(['F1|10 col 2| green', 'F1|20 col 4| blue'],
\ getline(1, '$'))
Xclose
set quickfixtextfunc&
" use a lambda function that returns a list with empty strings
set quickfixtextfunc={d\ ->\ ['',\ '']}
Xexpr ['F1:10:2:green', 'F1:20:4:blue']
Xwindow
call assert_equal(['F1|10 col 2| green', 'F1|20 col 4| blue'],
\ getline(1, '$'))
Xclose
set quickfixtextfunc&
" set the per-quickfix list text function to a lambda function
call g:Xsetlist([], ' ',
\ {'quickfixtextfunc' :
\ {d -> map(g:Xgetlist({'id' : d.id, 'items' : 1}).items[d.start_idx-1:d.end_idx-1],
\ "'Line ' .. v:val.lnum .. ', Col ' .. v:val.col")}})
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 assert_match("function('<lambda>\\d\\+')", string(g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc))
call g:Xsetlist([], 'f')
endfunc endfunc
func Test_qftextfunc() func Test_qftextfunc()