diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 21:52:58 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 21:52:58 +0000 |
commit | 931bffbda3668ddc609fc1da8f9eb576b170aa52 (patch) | |
tree | d8c1843a95da5ea0bb4acc09f7e37843d9995c86 /src/nvim/quickfix.c | |
parent | 142d9041391780ac15b89886a54015fdc5c73995 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-931bffbda3668ddc609fc1da8f9eb576b170aa52.tar.gz rneovim-931bffbda3668ddc609fc1da8f9eb576b170aa52.tar.bz2 rneovim-931bffbda3668ddc609fc1da8f9eb576b170aa52.zip |
Merge remote-tracking branch 'upstream/master' into userreguserreg
Diffstat (limited to 'src/nvim/quickfix.c')
-rw-r--r-- | src/nvim/quickfix.c | 609 |
1 files changed, 357 insertions, 252 deletions
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 5518fdfa51..4e20eb8925 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - // quickfix.c: functions for quickfix mode, using a file with error messages #include <assert.h> @@ -14,7 +11,7 @@ #include <time.h> #include "nvim/arglist.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/charset.h" @@ -23,7 +20,6 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/eval/window.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" @@ -33,34 +29,37 @@ #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/func_attr.h" +#include "nvim/garray.h" #include "nvim/gettext.h" #include "nvim/globals.h" #include "nvim/help.h" -#include "nvim/highlight_defs.h" +#include "nvim/highlight.h" #include "nvim/highlight_group.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/mark.h" #include "nvim/mbyte.h" -#include "nvim/memfile_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" +#include "nvim/option_defs.h" +#include "nvim/option_vars.h" #include "nvim/optionstr.h" -#include "nvim/os/fs_defs.h" +#include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/path.h" -#include "nvim/pos.h" +#include "nvim/pos_defs.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/search.h" #include "nvim/strings.h" -#include "nvim/types.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #include "nvim/window.h" struct dir_stack_T { @@ -80,14 +79,14 @@ struct qfline_S { int qf_col; ///< column where the error occurred int qf_end_col; ///< column when the error has range or zero int qf_nr; ///< error number - char *qf_module; ///< module name for this error - char *qf_pattern; ///< search pattern for the error - char *qf_text; ///< description of the error - char qf_viscol; ///< set to true if qf_col and qf_end_col is - // screen column - char qf_cleared; ///< set to true if line has been deleted - char qf_type; ///< type of the error (mostly 'E'); 1 for :helpgrep - char qf_valid; ///< valid error message detected + char *qf_module; ///< module name for this error + char *qf_pattern; ///< search pattern for the error + char *qf_text; ///< description of the error + char qf_viscol; ///< set to true if qf_col and qf_end_col is screen column + char qf_cleared; ///< set to true if line has been deleted + char qf_type; ///< type of the error (mostly 'E'); 1 for :helpgrep + typval_T qf_user_data; ///< custom user data associated with this item + char qf_valid; ///< valid error message detected }; // There is a stack of error lists. @@ -109,18 +108,19 @@ typedef enum { /// created using setqflist()/setloclist() with a title and/or user context /// information and entries can be added later using setqflist()/setloclist(). typedef struct qf_list_S { - unsigned qf_id; ///< Unique identifier for this list + unsigned qf_id; ///< Unique identifier for this list qfltype_T qfl_type; - qfline_T *qf_start; ///< pointer to the first error - qfline_T *qf_last; ///< pointer to the last error - qfline_T *qf_ptr; ///< pointer to the current error - int qf_count; ///< number of errors (0 means empty list) - int qf_index; ///< current index in the error list - int qf_nonevalid; ///< true if not a single valid entry found - char *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 - Callback qf_qftf_cb; ///< 'quickfixtextfunc' callback function + qfline_T *qf_start; ///< pointer to the first error + qfline_T *qf_last; ///< pointer to the last error + qfline_T *qf_ptr; ///< pointer to the current error + int qf_count; ///< number of errors (0 means empty list) + int qf_index; ///< current index in the error list + bool qf_nonevalid; ///< true if not a single valid entry found + bool qf_has_user_data; ///< true if at least one item has user_data attached + char *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 + Callback qf_qftf_cb; ///< 'quickfixtextfunc' callback function struct dir_stack_T *qf_dir_stack; char *qf_directory; @@ -129,7 +129,7 @@ typedef struct qf_list_S { bool qf_multiline; bool qf_multiignore; bool qf_multiscan; - long qf_changedtick; + int qf_changedtick; } qf_list_T; /// Quickfix/Location list stack definition @@ -150,7 +150,7 @@ struct qf_info_S { static qf_info_T ql_info; // global quickfix list static unsigned last_qf_id = 0; // Last Used quickfix list id -#define FMT_PATTERNS 13 // maximum number of % recognized +#define FMT_PATTERNS 14 // maximum number of % recognized // Structure used to hold the info of one part of 'errorformat' typedef struct efm_S efm_T; @@ -215,6 +215,7 @@ typedef struct { typedef struct { char *namebuf; + int bnr; char *module; char *errmsg; size_t errmsglen; @@ -226,12 +227,13 @@ typedef struct { char *pattern; int enr; char type; + typval_T *user_data; bool valid; } qffields_T; /// :vimgrep command arguments typedef struct vgr_args_S { - long tomatch; ///< maximum number of matches to find + int tomatch; ///< maximum number of matches to find char *spat; ///< search pattern int flags; ///< search modifier char **fnames; ///< list of files to search @@ -244,7 +246,13 @@ typedef struct vgr_args_S { # include "quickfix.c.generated.h" #endif -static char *e_no_more_items = N_("E553: No more items"); +static const char *e_no_more_items = N_("E553: No more items"); +static const char *e_current_quickfix_list_was_changed = + N_("E925: Current quickfix list was changed"); +static const char *e_current_location_list_was_changed = + N_("E926: Current location list was changed"); + +enum { QF_WINHEIGHT = 10, }; ///< default height for quickfix window // Quickfix window check helper macro #define IS_QF_WINDOW(wp) (bt_quickfix((wp)->w_buffer) && (wp)->w_llist_ref == NULL) @@ -257,10 +265,8 @@ static char *e_no_more_items = N_("E553: No more items"); #define IS_QF_LIST(qfl) ((qfl)->qfl_type == QFLT_QUICKFIX) #define IS_LL_LIST(qfl) ((qfl)->qfl_type == QFLT_LOCATION) -// // Return location list for window 'wp' // For location list window, return the referenced location list -// #define GET_LOC_LIST(wp) (IS_LL_WINDOW(wp) ? (wp)->w_llist_ref : (wp)->w_llist) // Macro to loop through all the items in a quickfix list @@ -275,10 +281,38 @@ static char *e_no_more_items = N_("E553: No more items"); static char *qf_last_bufname = NULL; static bufref_T qf_last_bufref = { NULL, 0, 0 }; -static char *e_current_quickfix_list_was_changed = - N_("E925: Current quickfix list was changed"); -static char *e_current_location_list_was_changed = - N_("E926: Current location list was changed"); +static garray_T qfga; + +/// Get a growarray to buffer text in. Shared between various commands to avoid +/// many alloc/free calls. +static garray_T *qfga_get(void) +{ + static bool initialized = false; + + if (!initialized) { + initialized = true; + ga_init(&qfga, 1, 256); + } + + // Reset the length to zero. Retain ga_data from previous use to avoid + // many alloc/free calls. + qfga.ga_len = 0; + + return &qfga; +} + +/// The "qfga" grow array buffer is reused across multiple quickfix commands as +/// a temporary buffer to reduce the number of alloc/free calls. But if the +/// buffer size is large, then to avoid holding on to that memory, clear the +/// grow array. Otherwise just reset the grow array length. +static void qfga_clear(void) +{ + if (qfga.ga_maxlen > 1000) { + ga_clear(&qfga); + } else { + qfga.ga_len = 0; + } +} // Counter to prevent autocmds from freeing up location lists when they are // still being used. @@ -309,7 +343,7 @@ static int qf_init_process_nextline(qf_list_T *qfl, efm_T *fmt_first, qfstate_T : ((qfl->qf_currfile != NULL && fields->valid) ? qfl->qf_currfile : NULL), fields->module, - 0, + fields->bnr, fields->errmsg, fields->lnum, fields->end_lnum, @@ -319,6 +353,7 @@ static int qf_init_process_nextline(qf_list_T *qfl, efm_T *fmt_first, qfstate_T fields->pattern, fields->enr, fields->type, + fields->user_data, fields->valid); } @@ -342,8 +377,8 @@ int qf_init(win_T *wp, const char *restrict efile, char *restrict errorformat, i qi = ll_get_or_alloc_list(wp); } - return qf_init_ext(qi, qi->qf_curlist, (char *)efile, curbuf, NULL, errorformat, - newlist, (linenr_T)0, (linenr_T)0, (char *)qf_title, enc); + return qf_init_ext(qi, qi->qf_curlist, efile, curbuf, NULL, errorformat, + newlist, 0, 0, qf_title, enc); } // Maximum number of bytes allowed per line while reading an errorfile. @@ -355,20 +390,21 @@ static struct fmtpattern { char *pattern; } fmt_pat[FMT_PATTERNS] = { { 'f', ".\\+" }, // only used when at end - { 'n', "\\d\\+" }, // 1 - { 'l', "\\d\\+" }, // 2 - { 'e', "\\d\\+" }, // 3 - { 'c', "\\d\\+" }, // 4 - { 'k', "\\d\\+" }, // 5 - { 't', "." }, // 6 -#define FMT_PATTERN_M 7 - { 'm', ".\\+" }, // 7 -#define FMT_PATTERN_R 8 - { 'r', ".*" }, // 8 - { 'p', "[- \t.]*" }, // 9 - { 'v', "\\d\\+" }, // 10 - { 's', ".\\+" }, // 11 - { 'o', ".\\+" } // 12 + { 'b', "\\d\\+" }, // 1 + { 'n', "\\d\\+" }, // 2 + { 'l', "\\d\\+" }, // 3 + { 'e', "\\d\\+" }, // 4 + { 'c', "\\d\\+" }, // 5 + { 'k', "\\d\\+" }, // 6 + { 't', "." }, // 7 +#define FMT_PATTERN_M 8 + { 'm', ".\\+" }, // 8 +#define FMT_PATTERN_R 9 + { 'r', ".*" }, // 9 + { 'p', "[-\t .]*" }, // 10 + { 'v', "\\d\\+" }, // 11 + { 's', ".\\+" }, // 12 + { 'o', ".\\+" } // 13 }; /// Convert an errorformat pattern to a regular expression pattern. @@ -499,7 +535,7 @@ static int efm_to_regpat(const char *efm, int len, efm_T *fmt_ptr, char *regpat) } } if (idx < FMT_PATTERNS) { - ptr = efmpat_to_regpat((char *)efmp, ptr, fmt_ptr, idx, round); + ptr = efmpat_to_regpat(efmp, ptr, fmt_ptr, idx, round); if (ptr == NULL) { return FAIL; } @@ -726,7 +762,7 @@ static int qf_get_next_buf_line(qfstate_T *state) if (state->buflnum > state->lnumlast) { return QF_END_OF_INPUT; } - char *p_buf = ml_get_buf(state->buf, state->buflnum, false); + char *p_buf = ml_get_buf(state->buf, state->buflnum); state->buflnum += 1; size_t len = strlen(p_buf); @@ -768,7 +804,7 @@ retry: memcpy(state->growbuf, IObuff, IOSIZE - 1); size_t growbuflen = state->linelen; - for (;;) { + while (true) { errno = 0; if (fgets(state->growbuf + growbuflen, (int)(state->growbufsiz - growbuflen), state->fd) == NULL) { @@ -788,7 +824,7 @@ retry: } state->growbufsiz = (2 * state->growbufsiz < LINE_MAXLEN) - ? 2 * state->growbufsiz : LINE_MAXLEN; + ? 2 * state->growbufsiz : LINE_MAXLEN; state->growbuf = xrealloc(state->growbuf, state->growbufsiz); } @@ -825,7 +861,7 @@ retry: state->linebuf = line; state->growbuf = line; state->growbufsiz = state->linelen < LINE_MAXLEN - ? state->linelen : LINE_MAXLEN; + ? state->linelen : LINE_MAXLEN; } } } @@ -1209,7 +1245,7 @@ static char *qf_cmdtitle(char *cmd) { static char qftitle_str[IOSIZE]; - snprintf((char *)qftitle_str, IOSIZE, ":%s", cmd); + snprintf(qftitle_str, IOSIZE, ":%s", cmd); return qftitle_str; } @@ -1249,6 +1285,7 @@ static void qf_new_list(qf_info_T *qi, const char *qf_title) qf_store_title(qfl, qf_title); qfl->qfl_type = qi->qfl_type; qfl->qf_id = ++last_qf_id; + qfl->qf_has_user_data = false; } /// Parse the match for filename ('%f') pattern in regmatch. @@ -1275,6 +1312,21 @@ static int qf_parse_fmt_f(regmatch_T *rmp, int midx, qffields_T *fields, int pre return QF_OK; } +/// Parse the match for buffer number ('%b') pattern in regmatch. +/// Return the matched value in "fields->bnr". +static int qf_parse_fmt_b(regmatch_T *rmp, int midx, qffields_T *fields) +{ + if (rmp->startp[midx] == NULL) { + return QF_FAIL; + } + int bnr = (int)atol(rmp->startp[midx]); + if (buflist_findnr(bnr) == NULL) { + return QF_FAIL; + } + fields->bnr = bnr; + return QF_OK; +} + /// Parse the match for error number ('%n') pattern in regmatch. /// Return the matched value in "fields->enr". static int qf_parse_fmt_n(regmatch_T *rmp, int midx, qffields_T *fields) @@ -1459,6 +1511,7 @@ static int qf_parse_fmt_o(regmatch_T *rmp, int midx, qffields_T *fields) /// Keep in sync with fmt_pat[]. static int (*qf_parse_fmt[FMT_PATTERNS])(regmatch_T *, int, qffields_T *) = { NULL, // %f + qf_parse_fmt_b, qf_parse_fmt_n, qf_parse_fmt_l, qf_parse_fmt_e, @@ -1531,6 +1584,7 @@ static int qf_parse_get_fields(char *linebuf, size_t linelen, efm_T *fmt_ptr, qf } fields->namebuf[0] = NUL; + fields->bnr = 0; fields->module[0] = NUL; fields->pattern[0] = NUL; if (!qf_multiscan) { @@ -1549,7 +1603,7 @@ static int qf_parse_get_fields(char *linebuf, size_t linelen, efm_T *fmt_ptr, qf // Always ignore case when looking for a matching error. regmatch.rm_ic = true; regmatch.regprog = fmt_ptr->prog; - int r = vim_regexec(®match, linebuf, (colnr_T)0); + int r = vim_regexec(®match, linebuf, 0); fmt_ptr->prog = regmatch.regprog; int status = QF_FAIL; if (r) { @@ -1624,7 +1678,7 @@ static int qf_parse_multiline_pfx(int idx, qf_list_T *qfl, qffields_T *fields) } if (*fields->errmsg) { size_t textlen = strlen(qfprev->qf_text); - size_t errlen = strlen(fields->errmsg); + size_t errlen = strlen(fields->errmsg); qfprev->qf_text = xrealloc(qfprev->qf_text, textlen + errlen + 2); qfprev->qf_text[textlen] = '\n'; STRCPY(qfprev->qf_text + textlen + 1, fields->errmsg); @@ -1804,12 +1858,14 @@ void check_quickfix_busy(void) /// @param pattern search pattern /// @param nr error number /// @param type type character +/// @param user_data custom user data or NULL /// @param valid valid entry /// /// @return QF_OK on success or QF_FAIL on failure. static int qf_add_entry(qf_list_T *qfl, char *dir, char *fname, char *module, int bufnum, char *mesg, linenr_T lnum, linenr_T end_lnum, int col, int end_col, - char vis_col, char *pattern, int nr, char type, char valid) + char vis_col, char *pattern, int nr, char type, typval_T *user_data, + char valid) { qfline_T *qfp = xmalloc(sizeof(qfline_T)); @@ -1830,6 +1886,12 @@ static int qf_add_entry(qf_list_T *qfl, char *dir, char *fname, char *module, in qfp->qf_col = col; qfp->qf_end_col = end_col; qfp->qf_viscol = vis_col; + if (user_data == NULL || user_data->v_type == VAR_UNKNOWN) { + qfp->qf_user_data.v_type = VAR_UNKNOWN; + } else { + tv_copy(user_data, &qfp->qf_user_data); + qfl->qf_has_user_data = true; + } if (pattern == NULL || *pattern == NUL) { qfp->qf_pattern = NULL; } else { @@ -1965,6 +2027,7 @@ static int copy_loclist_entries(const qf_list_T *from_qfl, qf_list_T *to_qfl) from_qfp->qf_pattern, from_qfp->qf_nr, 0, + &from_qfp->qf_user_data, from_qfp->qf_valid) == QF_FAIL) { return FAIL; } @@ -1990,6 +2053,7 @@ static int copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl) // Some of the fields are populated by qf_add_entry() to_qfl->qfl_type = from_qfl->qfl_type; to_qfl->qf_nonevalid = from_qfl->qf_nonevalid; + to_qfl->qf_has_user_data = from_qfl->qf_has_user_data; to_qfl->qf_count = 0; to_qfl->qf_index = 0; to_qfl->qf_start = NULL; @@ -2018,7 +2082,7 @@ static int copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl) // Assign a new ID for the location list to_qfl->qf_id = ++last_qf_id; - to_qfl->qf_changedtick = 0L; + to_qfl->qf_changedtick = 0; // When no valid entries are present in the list, qf_ptr points to // the first item in the list @@ -2107,7 +2171,7 @@ static int qf_get_fnum(qf_list_T *qfl, char *directory, char *fname) xfree(ptr); } else { xfree(qf_last_bufname); - buf = buflist_new(bufname, NULL, (linenr_T)0, BLN_NOOPT); + buf = buflist_new(bufname, NULL, 0, BLN_NOOPT); qf_last_bufname = (bufname == ptr) ? bufname : xstrdup(bufname); set_bufref(&qf_last_bufref, buf); } @@ -2259,7 +2323,7 @@ static char *qf_guess_filepath(qf_list_T *qfl, char *filename) } /// Returns true, if a quickfix/location list with the given identifier exists. -static bool qflist_valid(win_T *wp, unsigned int qf_id) +static bool qflist_valid(win_T *wp, unsigned qf_id) { qf_info_T *qi = &ql_info; @@ -2353,7 +2417,7 @@ static qfline_T *get_nth_valid_entry(qf_list_T *qfl, int errornr, int dir, int * { qfline_T *qf_ptr = qfl->qf_ptr; int qf_idx = qfl->qf_index; - char *err = e_no_more_items; + const char *err = e_no_more_items; while (errornr--) { qfline_T *prev_qf_ptr = qf_ptr; @@ -2549,7 +2613,7 @@ static int qf_open_new_file_win(qf_info_T *ll_ref) if (win_split(0, flags) == FAIL) { return FAIL; // not enough room for window } - p_swb = empty_option; // don't split again + p_swb = empty_string_option; // don't split again swb_flags = 0; RESET_BINDING(curwin); if (ll_ref != NULL) { @@ -2609,7 +2673,7 @@ static void qf_goto_win_with_qfl_file(int qf_fnum) { win_T *win = curwin; win_T *altwin = NULL; - for (;;) { + while (true) { if (win->w_buffer->b_fnum == qf_fnum) { break; } @@ -2709,7 +2773,7 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int int *opened_window) { qf_list_T *qfl = qf_get_curlist(qi); - long old_changetick = qfl->qf_changedtick; + int old_changetick = qfl->qf_changedtick; int old_qf_curlist = qi->qf_curlist; qfltype_T qfl_type = qfl->qfl_type; int retval = OK; @@ -2722,11 +2786,11 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int no_write_message(); return FAIL; } - retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1, + retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, 1, ECMD_HIDE + ECMD_SET_HELP, prev_winid == curwin->handle ? curwin : NULL); } else { - retval = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1, + retval = buflist_getfile(qf_ptr->qf_fnum, 1, GETF_SETMARK | GETF_SWITCH, forceit); } // If a location list, check whether the associated window is still @@ -2746,8 +2810,8 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int return QF_ABORT; } - if (old_qf_curlist != qi->qf_curlist // -V560 - || old_changetick != qfl->qf_changedtick // -V560 + if (old_qf_curlist != qi->qf_curlist + || old_changetick != qfl->qf_changedtick || !is_qf_entry_present(qfl, qf_ptr)) { if (qfl_type == QFLT_QUICKFIX) { emsg(_(e_current_quickfix_list_was_changed)); @@ -2789,7 +2853,7 @@ static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char qf_viscol, char // Move the cursor to the first line in the buffer pos_T save_cursor = curwin->w_cursor; curwin->w_cursor.lnum = 0; - if (!do_search(NULL, '/', '/', qf_pattern, (long)1, SEARCH_KEEP, NULL)) { + if (!do_search(NULL, '/', '/', qf_pattern, 1, SEARCH_KEEP, NULL)) { curwin->w_cursor = save_cursor; } } @@ -2799,6 +2863,8 @@ static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char qf_viscol, char static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf_T *old_curbuf, linenr_T old_lnum) { + garray_T *const gap = qfga_get(); + // Update the screen before showing the message, unless the screen // scrolled up. if (!msg_scrolled) { @@ -2807,13 +2873,14 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf update_screen(); } } - snprintf(IObuff, IOSIZE, _("(%d of %d)%s%s: "), qf_index, - qf_get_curlist(qi)->qf_count, - qf_ptr->qf_cleared ? _(" (line deleted)") : "", - qf_types(qf_ptr->qf_type, qf_ptr->qf_nr)); + vim_snprintf(IObuff, IOSIZE, _("(%d of %d)%s%s: "), qf_index, + qf_get_curlist(qi)->qf_count, + qf_ptr->qf_cleared ? _(" (line deleted)") : "", + qf_types(qf_ptr->qf_type, qf_ptr->qf_nr)); // Add the message, skipping leading whitespace and newlines. - int len = (int)strlen(IObuff); - qf_fmt_text(skipwhite(qf_ptr->qf_text), IObuff + len, IOSIZE - len); + ga_concat(gap, IObuff); + qf_fmt_text(gap, skipwhite(qf_ptr->qf_text)); + ga_append(gap, NUL); // Output the message. Overwrite to avoid scrolling when the 'O' // flag is present in 'shortmess'; But when not jumping, print the @@ -2825,8 +2892,10 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf msg_scroll = false; } msg_ext_set_kind("quickfix"); - msg_attr_keep(IObuff, 0, true, false); + msg_attr_keep(gap->ga_data, 0, true, false); msg_scroll = (int)i; + + qfga_clear(); } /// Find a usable window for opening a file from the quickfix/location list. If @@ -2839,7 +2908,7 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int *opened_window) { qf_list_T *qfl = qf_get_curlist(qi); - long old_changetick = qfl->qf_changedtick; + int old_changetick = qfl->qf_changedtick; int old_qf_curlist = qi->qf_curlist; qfltype_T qfl_type = qfl->qfl_type; @@ -2850,7 +2919,7 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int } } if (old_qf_curlist != qi->qf_curlist - || old_changetick != qfl->qf_changedtick // -V560 + || old_changetick != qfl->qf_changedtick || !is_qf_entry_present(qfl, qf_ptr)) { if (qfl_type == QFLT_QUICKFIX) { emsg(_(e_current_quickfix_list_was_changed)); @@ -3021,7 +3090,7 @@ theend: qfl->qf_ptr = qf_ptr; qfl->qf_index = qf_index; } - if (p_swb != old_swb && p_swb == empty_option) { + if (p_swb != old_swb && p_swb == empty_string_option) { // Restore old 'switchbuf' value, but not when an autocommand or // modeline has changed the value. p_swb = old_swb; @@ -3081,46 +3150,35 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) } msg_putchar('\n'); - msg_outtrans_attr(IObuff, cursel ? HL_ATTR(HLF_QFL) : qfFileAttr); + msg_outtrans(IObuff, cursel ? HL_ATTR(HLF_QFL) : qfFileAttr); if (qfp->qf_lnum != 0) { msg_puts_attr(":", qfSepAttr); } - if (qfp->qf_lnum == 0) { - IObuff[0] = NUL; - } else { - qf_range_text(qfp, IObuff, IOSIZE); + garray_T *gap = qfga_get(); + if (qfp->qf_lnum != 0) { + qf_range_text(gap, qfp); } - vim_snprintf(IObuff + strlen(IObuff), IOSIZE, "%s", qf_types(qfp->qf_type, qfp->qf_nr)); - msg_puts_attr((const char *)IObuff, qfLineAttr); + ga_concat(gap, qf_types(qfp->qf_type, qfp->qf_nr)); + ga_append(gap, NUL); + msg_puts_attr(gap->ga_data, qfLineAttr); msg_puts_attr(":", qfSepAttr); if (qfp->qf_pattern != NULL) { - qf_fmt_text(qfp->qf_pattern, IObuff, IOSIZE); - msg_puts((const char *)IObuff); + gap = qfga_get(); + qf_fmt_text(gap, qfp->qf_pattern); + ga_append(gap, NUL); + msg_puts(gap->ga_data); msg_puts_attr(":", qfSepAttr); } msg_puts(" "); - char *tbuf = IObuff; - size_t tbuflen = IOSIZE; - size_t len = strlen(qfp->qf_text) + 3; - - if (len > IOSIZE) { - tbuf = xmalloc(len); - tbuflen = 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((fname != NULL || qfp->qf_lnum != 0) - ? skipwhite(qfp->qf_text) : qfp->qf_text, - tbuf, (int)tbuflen); - msg_prt_line(tbuf, false); - - if (tbuf != IObuff) { - xfree(tbuf); - } + gap = qfga_get(); + qf_fmt_text(gap, (fname != NULL || qfp->qf_lnum != 0) ? skipwhite(qfp->qf_text) : qfp->qf_text); + ga_append(gap, NUL); + msg_prt_line(gap->ga_data, false); } // ":clist": list all errors @@ -3195,51 +3253,53 @@ void qf_list(exarg_T *eap) } os_breakcheck(); } + qfga_clear(); } -// Remove newlines and leading whitespace from an error message. -// Put the result in "buf[bufsize]". -static void qf_fmt_text(const char *restrict text, char *restrict buf, int bufsize) +/// Remove newlines and leading whitespace from an error message. +/// Add the result to the grow array "gap". +static void qf_fmt_text(garray_T *gap, const char *restrict text) FUNC_ATTR_NONNULL_ALL { - int i; - const char *p = (char *)text; - - for (i = 0; *p != NUL && i < bufsize - 1; i++) { + const char *p = text; + while (*p != NUL) { if (*p == '\n') { - buf[i] = ' '; + ga_append(gap, ' '); while (*++p != NUL) { if (!ascii_iswhite(*p) && *p != '\n') { break; } } } else { - buf[i] = *p++; + ga_append(gap, (uint8_t)(*p++)); } } - buf[i] = NUL; } -// Range information from lnum, col, end_lnum, and end_col. -// Put the result in "buf[bufsize]". -static void qf_range_text(const qfline_T *qfp, char *buf, int bufsize) +/// Add the range information from the lnum, col, end_lnum, and end_col values +/// of a quickfix entry to the grow array "gap". +static void qf_range_text(garray_T *gap, const qfline_T *qfp) { - vim_snprintf(buf, (size_t)bufsize, "%" PRIdLINENR, qfp->qf_lnum); - int len = (int)strlen(buf); + char *const buf = IObuff; + const size_t bufsize = IOSIZE; + + vim_snprintf(buf, bufsize, "%" PRIdLINENR, qfp->qf_lnum); + size_t len = strlen(buf); if (qfp->qf_end_lnum > 0 && qfp->qf_lnum != qfp->qf_end_lnum) { - vim_snprintf(buf + len, (size_t)(bufsize - len), "-%" PRIdLINENR, qfp->qf_end_lnum); - len += (int)strlen(buf + len); + vim_snprintf(buf + len, bufsize - len, "-%" PRIdLINENR, qfp->qf_end_lnum); + len += strlen(buf + len); } if (qfp->qf_col > 0) { - vim_snprintf(buf + len, (size_t)(bufsize - len), " col %d", qfp->qf_col); - len += (int)strlen(buf + len); + vim_snprintf(buf + len, bufsize - len, " col %d", qfp->qf_col); + len += strlen(buf + len); if (qfp->qf_end_col > 0 && qfp->qf_col != qfp->qf_end_col) { - vim_snprintf(buf + len, (size_t)(bufsize - len), "-%d", qfp->qf_end_col); - len += (int)strlen(buf + len); + vim_snprintf(buf + len, bufsize - len, "-%d", qfp->qf_end_col); + len += strlen(buf + len); } } - buf[len] = NUL; + + ga_concat_len(gap, buf, len); } /// Display information (list number, list size and the title) about a @@ -3250,7 +3310,7 @@ static void qf_msg(qf_info_T *qi, int which, char *lead) int count = qi->qf_lists[which].qf_count; char buf[IOSIZE]; - vim_snprintf((char *)buf, IOSIZE, _("%serror list %d of %d; %d errors "), + vim_snprintf(buf, IOSIZE, _("%serror list %d of %d; %d errors "), lead, which + 1, qi->qf_listcount, @@ -3266,7 +3326,7 @@ static void qf_msg(qf_info_T *qi, int which, char *lead) xstrlcat(buf, title, IOSIZE); } trunc_string(buf, buf, Columns - 1, IOSIZE); - msg(buf); + msg(buf, 0); } /// ":colder [count]": Up in the quickfix stack. @@ -3325,7 +3385,7 @@ void qf_history(exarg_T *eap) } if (qf_stack_empty(qi)) { - msg(_("No entries")); + msg(_("No entries"), 0); } else { for (int i = 0; i < qi->qf_listcount; i++) { qf_msg(qi, i, i == qi->qf_curlist ? "> " : " "); @@ -3346,6 +3406,7 @@ static void qf_free_items(qf_list_T *qfl) xfree(qfp->qf_module); xfree(qfp->qf_text); xfree(qfp->qf_pattern); + tv_clear(&qfp->qf_user_data); stop = (qfp == qfpnext); xfree(qfp); if (stop) { @@ -3353,9 +3414,10 @@ static void qf_free_items(qf_list_T *qfl) // to avoid crashing when it's wrong. // TODO(vim): Avoid qf_count being incorrect. qfl->qf_count = 1; + } else { + qfl->qf_start = qfpnext; } } - qfl->qf_start = qfpnext; qfl->qf_count--; } @@ -3387,17 +3449,20 @@ static void qf_free(qf_list_T *qfl) qfl->qf_ctx = NULL; callback_free(&qfl->qf_qftf_cb); qfl->qf_id = 0; - qfl->qf_changedtick = 0L; + qfl->qf_changedtick = 0; } -// qf_mark_adjust: adjust marks -bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount, +/// Adjust error list entries for changed line numbers +/// +/// Note: `buf` is the changed buffer, but `wp` is a potential location list +/// into that buffer, or NULL to check the quickfix list. +bool qf_mark_adjust(buf_T *buf, win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount, linenr_T amount_after) { qf_info_T *qi = &ql_info; int buf_has_flag = wp == NULL ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY; - if (!(curbuf->b_has_qf_entry & buf_has_flag)) { + if (!(buf->b_has_qf_entry & buf_has_flag)) { return false; } if (wp != NULL) { @@ -3414,7 +3479,7 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount, qf_list_T *qfl = qf_get_list(qi, idx); if (!qf_list_empty(qfl)) { FOR_ALL_QFL_ITEMS(qfl, qfp, i) { - if (qfp->qf_fnum == curbuf->b_fnum) { + if (qfp->qf_fnum == buf->b_fnum) { found_one = true; if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2) { if (amount == MAXLNUM) { @@ -3475,7 +3540,7 @@ static char *qf_types(int c, int nr) } static char buf[20]; - snprintf((char *)buf, sizeof(buf), "%s %3d", p, nr); + snprintf(buf, sizeof(buf), "%s %3d", p, nr); return buf; } @@ -3581,12 +3646,12 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz, bool vertsp static void qf_set_cwindow_options(void) { // switch off 'swapfile' - set_option_value_give_err("swf", 0L, NULL, OPT_LOCAL); - set_option_value_give_err("bt", 0L, "quickfix", OPT_LOCAL); - set_option_value_give_err("bh", 0L, "hide", OPT_LOCAL); + set_option_value_give_err("swf", BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value_give_err("bt", STATIC_CSTR_AS_OPTVAL("quickfix"), OPT_LOCAL); + set_option_value_give_err("bh", STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); RESET_BINDING(curwin); curwin->w_p_diff = false; - set_option_value_give_err("fdm", 0L, "manual", OPT_LOCAL); + set_option_value_give_err("fdm", STATIC_CSTR_AS_OPTVAL("manual"), OPT_LOCAL); } // Open a new quickfix or location list window, load the quickfix buffer and @@ -3800,13 +3865,11 @@ static bool qf_win_pos_update(qf_info_T *qi, int old_qf_index) static int is_qf_win(const win_T *win, const qf_info_T *qi) FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - // // A window displaying the quickfix buffer will have the w_llist_ref field // set to NULL. // A window displaying a location list buffer will have the w_llist_ref // pointing to the location list. - // - if (bt_quickfix(win->w_buffer)) { + if (buf_valid(win->w_buffer) && bt_quickfix(win->w_buffer)) { if ((IS_QF_STACK(qi) && win->w_llist_ref == NULL) || (IS_LL_STACK(qi) && win->w_llist_ref == qi)) { return true; @@ -3854,11 +3917,12 @@ static buf_T *qf_find_buf(qf_info_T *qi) } /// Process the 'quickfixtextfunc' option value. -void qf_process_qftf_option(char **errmsg) +const char *did_set_quickfixtextfunc(optset_T *args FUNC_ATTR_UNUSED) { if (option_set_callback_func(p_qftf, &qftf_cb) == FAIL) { - *errmsg = e_invarg; + return e_invarg; } + return NULL; } /// Update the w:quickfix_title variable in the quickfix/location list window in @@ -3945,21 +4009,21 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli char *dirname, char *qftf_str, bool first_bufline) FUNC_ATTR_NONNULL_ARG(1, 2, 4, 5) { + garray_T *gap = qfga_get(); + // If the 'quickfixtextfunc' function returned a non-empty custom string // for this entry, then use it. if (qftf_str != NULL && *qftf_str != NUL) { - xstrlcpy(IObuff, qftf_str, IOSIZE); + ga_concat(gap, qftf_str); } else { buf_T *errbuf; - int len; if (qfp->qf_module != NULL) { - xstrlcpy(IObuff, qfp->qf_module, IOSIZE); - len = (int)strlen(IObuff); + ga_concat(gap, qfp->qf_module); } else if (qfp->qf_fnum != 0 && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL && errbuf->b_fname != NULL) { if (qfp->qf_type == 1) { // :helpgrep - xstrlcpy(IObuff, path_tail(errbuf->b_fname), IOSIZE); + ga_concat(gap, path_tail(errbuf->b_fname)); } else { // Shorten the file name if not done already. // For optimization, do this only for the first entry in a @@ -3972,48 +4036,38 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli } shorten_buf_fname(errbuf, dirname, false); } - xstrlcpy(IObuff, errbuf->b_fname, IOSIZE); + ga_concat(gap, errbuf->b_fname); } - len = (int)strlen(IObuff); - } else { - len = 0; } - if (len < IOSIZE - 1) { - IObuff[len++] = '|'; - } - if (qfp->qf_lnum > 0) { - qf_range_text(qfp, IObuff + len, IOSIZE - len); - len += (int)strlen(IObuff + len); - snprintf(IObuff + len, (size_t)(IOSIZE - len), "%s", qf_types(qfp->qf_type, - qfp->qf_nr)); - len += (int)strlen(IObuff + len); + ga_append(gap, '|'); + + if (qfp->qf_lnum > 0) { + qf_range_text(gap, qfp); + ga_concat(gap, qf_types(qfp->qf_type, qfp->qf_nr)); } 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++] = ' '; + qf_fmt_text(gap, qfp->qf_pattern); } + ga_append(gap, '|'); + ga_append(gap, ' '); // 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); + qf_fmt_text(gap, gap->ga_len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text); } - if (ml_append_buf(buf, lnum, IObuff, - (colnr_T)strlen(IObuff) + 1, false) == FAIL) { + ga_append(gap, NUL); + if (ml_append_buf(buf, lnum, gap->ga_data, gap->ga_len, false) == FAIL) { return FAIL; } + 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, int qf_winid, long start_idx, long end_idx) +static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, int start_idx, int end_idx) { Callback *cb = &qftf_cb; list_T *qftf_list = NULL; @@ -4078,7 +4132,12 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q // delete all existing lines while ((curbuf->b_ml.ml_flags & ML_EMPTY) == 0) { - (void)ml_delete((linenr_T)1, false); + // If deletion fails, this loop may run forever, so + // signal error and return. + if (ml_delete(1, false) == FAIL) { + internal_error("qf_fill_buffer()"); + return; + } } } @@ -4104,7 +4163,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q lnum = buf->b_ml.ml_line_count; } - list_T *qftf_list = call_qftf_func(qfl, qf_winid, lnum + 1, (long)qfl->qf_count); + list_T *qftf_list = call_qftf_func(qfl, qf_winid, lnum + 1, qfl->qf_count); listitem_T *qftf_li = tv_list_first(qftf_list); int prev_bufnr = -1; @@ -4142,6 +4201,8 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q // Delete the empty line which is now at the end (void)ml_delete(lnum + 1, false); } + + qfga_clear(); } // Correct cursor position. @@ -4152,7 +4213,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q // resembles reading a file into a buffer, it's more logical when using // autocommands. curbuf->b_ro_locked++; - set_option_value_give_err("ft", 0L, "qf", OPT_LOCAL); + set_option_value_give_err("ft", STATIC_CSTR_AS_OPTVAL("qf"), OPT_LOCAL); curbuf->b_p_ma = false; keep_filetype = true; // don't detect 'filetype' @@ -4263,11 +4324,11 @@ static char *make_get_fullcmd(const char *makecmd, const char *fname) len += strlen(p_sp) + strlen(fname) + 3; } char *const cmd = xmalloc(len); - snprintf(cmd, len, "%s%s%s", p_shq, (char *)makecmd, p_shq); + snprintf(cmd, len, "%s%s%s", p_shq, makecmd, p_shq); // If 'shellpipe' empty: don't redirect to 'errorfile'. if (*p_sp != NUL) { - append_redir(cmd, len, p_sp, (char *)fname); + append_redir(cmd, len, p_sp, fname); } // Display the fully formed command. Output a newline if there's something @@ -4278,7 +4339,7 @@ static char *make_get_fullcmd(const char *makecmd, const char *fname) } msg_start(); msg_puts(":!"); - msg_outtrans(cmd); // show what we are doing + msg_outtrans(cmd, 0); // show what we are doing return cmd; } @@ -4391,7 +4452,7 @@ static char *get_mef_name(void) } // Keep trying until the name doesn't exist yet. - for (;;) { + while (true) { if (start == -1) { start = (int)os_get_pid(); } else { @@ -5129,9 +5190,9 @@ static void vgr_display_fname(char *fname) msg_start(); char *p = msg_strtrunc(fname, true); if (p == NULL) { - msg_outtrans(fname); + msg_outtrans(fname, 0); } else { - msg_outtrans(p); + msg_outtrans(p, 0); xfree(p); } msg_clr_eos(); @@ -5148,7 +5209,7 @@ static buf_T *vgr_load_dummy_buf(char *fname, char *dirname_start, char *dirname // indent scripts, a great speed improvement. char *save_ei = au_event_disable(",Filetype"); - long save_mls = p_mls; + OptInt save_mls = p_mls; p_mls = 0; // Load file into a buffer, so that 'fileencoding' is detected, @@ -5187,11 +5248,14 @@ static bool vgr_qflist_valid(win_T *wp, qf_info_T *qi, unsigned qfid, char *titl /// Search for a pattern in all the lines in a buffer and add the matching lines /// to a quickfix list. static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *spat, - regmmatch_T *regmatch, long *tomatch, int duplicate_name, int flags) + regmmatch_T *regmatch, int *tomatch, int duplicate_name, int flags) FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5, 6) { bool found_match = false; - const size_t pat_len = strlen(spat); + size_t pat_len = strlen(spat); + if (pat_len > MAX_FUZZY_MATCHES) { + pat_len = MAX_FUZZY_MATCHES; + } for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count && *tomatch > 0; lnum++) { colnr_T col = 0; @@ -5206,7 +5270,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp fname, NULL, duplicate_name ? 0 : buf->b_fnum, - ml_get_buf(buf, regmatch->startpos[0].lnum + lnum, false), + ml_get_buf(buf, regmatch->startpos[0].lnum + lnum), regmatch->startpos[0].lnum + lnum, regmatch->endpos[0].lnum + lnum, regmatch->startpos[0].col + 1, @@ -5215,6 +5279,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp NULL, // search pattern 0, // nr 0, // type + NULL, // user_data true) // valid == QF_FAIL) { got_int = true; @@ -5228,17 +5293,18 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp break; } col = regmatch->endpos[0].col + (col == regmatch->endpos[0].col); - if (col > (colnr_T)strlen(ml_get_buf(buf, lnum, false))) { + if (col > (colnr_T)strlen(ml_get_buf(buf, lnum))) { break; } } } else { - char *const str = ml_get_buf(buf, lnum, false); + char *const str = ml_get_buf(buf, lnum); int score; uint32_t matches[MAX_FUZZY_MATCHES]; const size_t sz = sizeof(matches) / sizeof(matches[0]); // Fuzzy string match + CLEAR_FIELD(matches); while (fuzzy_match(str + col, spat, false, &score, matches, (int)sz) > 0) { // Pass the buffer number so that it gets used even for a // dummy buffer, unless duplicate_name is set, then the @@ -5257,6 +5323,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp NULL, // search pattern 0, // nr 0, // type + NULL, // user_data true) // valid == QF_FAIL) { got_int = true; @@ -5380,7 +5447,7 @@ static int vgr_process_files(win_T *wp, qf_info_T *qi, vgr_args_T *cmd_args, boo // ":lcd %:p:h" changes the meaning of short path names. os_dirname(dirname_start, MAXPATHL); - time_t seconds = (time_t)0; + time_t seconds = 0; for (int fi = 0; fi < cmd_args->fcount && !got_int && cmd_args->tomatch > 0; fi++) { char *fname = path_try_shorten_fname(cmd_args->fnames[fi]); if (time(NULL) > seconds) { @@ -5413,7 +5480,7 @@ static int vgr_process_files(win_T *wp, qf_info_T *qi, vgr_args_T *cmd_args, boo if (buf == NULL) { if (!got_int) { - smsg(_("Cannot open file \"%s\""), fname); + smsg(0, _("Cannot open file \"%s\""), fname); } } else { // Try for a match in all lines of the buffer. @@ -5618,7 +5685,7 @@ static void restore_start_dir(char *dirname_start) static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resulting_dir) { // Allocate a buffer without putting it in the buffer list. - buf_T *newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); + buf_T *newbuf = buflist_new(NULL, NULL, 1, BLN_DUMMY); if (newbuf == NULL) { return NULL; } @@ -5650,7 +5717,7 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin bufref_T newbuf_to_wipe; newbuf_to_wipe.br_buf = NULL; - int readfile_result = readfile(fname, NULL, (linenr_T)0, (linenr_T)0, + int readfile_result = readfile(fname, NULL, 0, 0, (linenr_T)MAXLNUM, NULL, READ_NEW | READ_DUMMY, false); newbuf->b_locked--; @@ -5772,28 +5839,21 @@ static int get_qfline_items(qfline_T *qfp, list_T *list) buf[0] = qfp->qf_type; buf[1] = NUL; if (tv_dict_add_nr(dict, S_LEN("bufnr"), (varnumber_T)bufnum) == FAIL - || (tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)qfp->qf_lnum) - == FAIL) - || (tv_dict_add_nr(dict, S_LEN("end_lnum"), (varnumber_T)qfp->qf_end_lnum) - == FAIL) + || (tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)qfp->qf_lnum) == FAIL) + || (tv_dict_add_nr(dict, S_LEN("end_lnum"), (varnumber_T)qfp->qf_end_lnum) == FAIL) || (tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)qfp->qf_col) == FAIL) - || (tv_dict_add_nr(dict, S_LEN("end_col"), (varnumber_T)qfp->qf_end_col) - == FAIL) - || (tv_dict_add_nr(dict, S_LEN("vcol"), (varnumber_T)qfp->qf_viscol) - == FAIL) + || (tv_dict_add_nr(dict, S_LEN("end_col"), (varnumber_T)qfp->qf_end_col) == FAIL) + || (tv_dict_add_nr(dict, S_LEN("vcol"), (varnumber_T)qfp->qf_viscol) == FAIL) || (tv_dict_add_nr(dict, S_LEN("nr"), (varnumber_T)qfp->qf_nr) == FAIL) - || (tv_dict_add_str(dict, S_LEN("module"), - (qfp->qf_module == NULL ? "" : (const char *)qfp->qf_module)) - == FAIL) - || (tv_dict_add_str(dict, S_LEN("pattern"), - (qfp->qf_pattern == NULL ? "" : (const char *)qfp->qf_pattern)) + || (tv_dict_add_str(dict, S_LEN("module"), (qfp->qf_module == NULL ? "" : qfp->qf_module)) == FAIL) - || (tv_dict_add_str(dict, S_LEN("text"), - (qfp->qf_text == NULL ? "" : (const char *)qfp->qf_text)) + || (tv_dict_add_str(dict, S_LEN("pattern"), (qfp->qf_pattern == NULL ? "" : qfp->qf_pattern)) == FAIL) - || (tv_dict_add_str(dict, S_LEN("type"), (const char *)buf) == FAIL) - || (tv_dict_add_nr(dict, S_LEN("valid"), (varnumber_T)qfp->qf_valid) - == FAIL)) { + || (tv_dict_add_str(dict, S_LEN("text"), (qfp->qf_text == NULL ? "" : qfp->qf_text)) == FAIL) + || (tv_dict_add_str(dict, S_LEN("type"), buf) == FAIL) + || (qfp->qf_user_data.v_type != VAR_UNKNOWN + && tv_dict_add_tv(dict, S_LEN("user_data"), &qfp->qf_user_data) == FAIL) + || (tv_dict_add_nr(dict, S_LEN("valid"), (varnumber_T)qfp->qf_valid) == FAIL)) { // tv_dict_add* fail only if key already exist, but this is a newly // allocated dictionary which is thus guaranteed to have no existing keys. abort(); @@ -5897,7 +5957,7 @@ static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict) qf_info_T *const qi = qf_alloc_stack(QFLT_INTERNAL); if (qf_init_ext(qi, 0, NULL, NULL, &di->di_tv, errorformat, - true, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) { + true, 0, 0, NULL, NULL) > 0) { (void)get_errorlist(qi, NULL, 0, 0, l); qf_free(&qi->qf_lists[0]); } @@ -6016,8 +6076,7 @@ static int qf_getprop_qfidx(qf_info_T *qi, dict_T *what) qf_idx = INVALID_QFIDX; } } - } else if (di->di_tv.v_type == VAR_STRING - && strequal((const char *)di->di_tv.vval.v_string, "$")) { + } else if (di->di_tv.v_type == VAR_STRING && strequal(di->di_tv.vval.v_string, "$")) { // Get the last quickfix list number qf_idx = qi->qf_listcount - 1; } else { @@ -6046,7 +6105,7 @@ static int qf_getprop_defaults(qf_info_T *qi, int flags, int locstack, dict_T *r int status = OK; if (flags & QF_GETLIST_TITLE) { - status = tv_dict_add_str(retdict, S_LEN("title"), (const char *)""); + status = tv_dict_add_str(retdict, S_LEN("title"), ""); } if ((status == OK) && (flags & QF_GETLIST_ITEMS)) { list_T *l = tv_list_alloc(kListLenMayKnow); @@ -6059,7 +6118,7 @@ static int qf_getprop_defaults(qf_info_T *qi, int flags, int locstack, dict_T *r status = tv_dict_add_nr(retdict, S_LEN("winid"), qf_winid(qi)); } if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) { - status = tv_dict_add_str(retdict, S_LEN("context"), (const char *)""); + status = tv_dict_add_str(retdict, S_LEN("context"), ""); } if ((status == OK) && (flags & QF_GETLIST_ID)) { status = tv_dict_add_nr(retdict, S_LEN("id"), 0); @@ -6089,8 +6148,7 @@ static int qf_getprop_defaults(qf_info_T *qi, int flags, int locstack, dict_T *r /// Return the quickfix list title as 'title' in retdict static int qf_getprop_title(qf_list_T *qfl, dict_T *retdict) { - return tv_dict_add_str(retdict, S_LEN("title"), - (const char *)qfl->qf_title); + return tv_dict_add_str(retdict, S_LEN("title"), qfl->qf_title); } // Returns the identifier of the window used to display files from a location @@ -6274,8 +6332,7 @@ static int qf_setprop_qftf(qf_list_T *qfl, dictitem_T *di) /// 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. -static int qf_add_entry_from_dict(qf_list_T *qfl, const dict_T *d, bool first_entry, - bool *valid_entry) +static int qf_add_entry_from_dict(qf_list_T *qfl, dict_T *d, bool first_entry, bool *valid_entry) FUNC_ATTR_NONNULL_ALL { static bool did_bufnr_emsg; @@ -6299,6 +6356,9 @@ static int qf_add_entry_from_dict(qf_list_T *qfl, const dict_T *d, bool first_en if (text == NULL) { text = xcalloc(1, 1); } + typval_T user_data = { .v_type = VAR_UNKNOWN }; + tv_dict_get_tv(d, "user_data", &user_data); + bool valid = true; if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) { @@ -6335,12 +6395,14 @@ static int qf_add_entry_from_dict(qf_list_T *qfl, const dict_T *d, bool first_en pattern, // search pattern nr, type == NULL ? NUL : *type, + &user_data, valid); xfree(filename); xfree(module); xfree(pattern); xfree(text); + tv_clear(&user_data); if (valid) { *valid_entry = true; @@ -6376,13 +6438,12 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char *title, continue; // Skip non-dict items. } - const dict_T *const d = TV_LIST_ITEM_TV(li)->vval.v_dict; + dict_T *const d = TV_LIST_ITEM_TV(li)->vval.v_dict; if (d == NULL) { continue; } - retval = qf_add_entry_from_dict(qfl, d, li == tv_list_first(list), - &valid_entry); + retval = qf_add_entry_from_dict(qfl, d, li == tv_list_first(list), &valid_entry); if (retval == QF_FAIL) { break; } @@ -6439,8 +6500,7 @@ static int qf_setprop_get_qfidx(const qf_info_T *qi, const dict_T *what, int act } else if (action != ' ') { *newlist = false; // use the specified list } - } else if (di->di_tv.v_type == VAR_STRING - && strequal((const char *)di->di_tv.vval.v_string, "$")) { + } else if (di->di_tv.v_type == VAR_STRING && strequal(di->di_tv.vval.v_string, "$")) { if (!qf_stack_empty(qi)) { qf_idx = qi->qf_listcount - 1; } else if (*newlist) { @@ -6525,7 +6585,7 @@ static int qf_setprop_items_from_lines(qf_info_T *qi, int qf_idx, const dict_T * qf_free_items(&qi->qf_lists[qf_idx]); } if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, errorformat, - false, (linenr_T)0, (linenr_T)0, NULL, NULL) >= 0) { + false, 0, 0, NULL, NULL) >= 0) { retval = OK; } @@ -6721,6 +6781,27 @@ int set_errorlist(win_T *wp, list_T *list, int action, char *title, dict_T *what return retval; } +static bool mark_quickfix_user_data(qf_info_T *qi, int copyID) +{ + bool abort = false; + for (int i = 0; i < LISTCOUNT && !abort; i++) { + qf_list_T *qfl = &qi->qf_lists[i]; + if (!qfl->qf_has_user_data) { + continue; + } + qfline_T *qfp; + int j; + FOR_ALL_QFL_ITEMS(qfl, qfp, j) { + typval_T *user_data = &qfp->qf_user_data; + if (user_data != NULL && user_data->v_type != VAR_NUMBER + && user_data->v_type != VAR_STRING && user_data->v_type != VAR_FLOAT) { + abort = abort || set_ref_in_item(user_data, copyID, NULL, NULL); + } + } + } + return abort; +} + /// Mark the quickfix context and callback function as in use for all the lists /// in a quickfix stack. static bool mark_quickfix_ctx(qf_info_T *qi, int copyID) @@ -6750,6 +6831,11 @@ bool set_ref_in_quickfix(int copyID) return abort; } + abort = mark_quickfix_user_data(&ql_info, copyID); + if (abort) { + return abort; + } + abort = set_ref_in_callback(&qftf_cb, copyID, NULL, NULL); if (abort) { return abort; @@ -6761,6 +6847,11 @@ bool set_ref_in_quickfix(int copyID) if (abort) { return abort; } + + abort = mark_quickfix_user_data(win->w_llist, copyID); + if (abort) { + return abort; + } } if (IS_LL_WINDOW(win) && (win->w_llist_ref->qf_refcount == 1)) { @@ -6946,18 +7037,18 @@ void ex_cexpr(exarg_T *eap) // Evaluate the expression. When the result is a string or a list we can // use it to fill the errorlist. - typval_T tv; - if (eval0(eap->arg, &tv, &eap->nextcmd, true) == FAIL) { + typval_T *tv = eval_expr(eap->arg, eap); + if (tv == NULL) { return; } - if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL) - || tv.v_type == VAR_LIST) { + if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL) + || tv->v_type == VAR_LIST) { incr_quickfix_busy(); - int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, &tv, p_efm, + int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, tv, p_efm, (eap->cmdidx != CMD_caddexpr && eap->cmdidx != CMD_laddexpr), - (linenr_T)0, (linenr_T)0, + 0, 0, qf_cmdtitle(*eap->cmdlinep), NULL); if (qf_stack_empty(qi)) { decr_quickfix_busy(); @@ -6985,7 +7076,7 @@ void ex_cexpr(exarg_T *eap) emsg(_("E777: String or List expected")); } cleanup: - tv_clear(&tv); + tv_free(tv); } // Get the location list for ":lhelpgrep" @@ -7018,7 +7109,7 @@ static void hgr_search_file(qf_list_T *qfl, char *fname, regmatch_T *p_regmatch) while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) { char *line = IObuff; - if (vim_regexec(p_regmatch, line, (colnr_T)0)) { + if (vim_regexec(p_regmatch, line, 0)) { int l = (int)strlen(line); // remove trailing CR, LF, spaces, etc. @@ -7041,7 +7132,8 @@ static void hgr_search_file(qf_list_T *qfl, char *fname, regmatch_T *p_regmatch) NULL, // search pattern 0, // nr 1, // type - true) // valid + NULL, // user_data + true) // valid == QF_FAIL) { got_int = true; if (line != IObuff) { @@ -7101,7 +7193,7 @@ static void hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, const char while (*p != NUL && !got_int) { copy_option_part(&p, NameBuff, MAXPATHL, ","); - hgr_search_files_in_dir(qfl, NameBuff, p_regmatch, (char *)lang); + hgr_search_files_in_dir(qfl, NameBuff, p_regmatch, lang); } } @@ -7130,7 +7222,7 @@ void ex_helpgrep(exarg_T *eap) // Make 'cpoptions' empty, the 'l' flag should not be used here. char *const save_cpo = p_cpo; const bool save_cpo_allocated = is_option_allocated("cpo"); - p_cpo = empty_option; + p_cpo = empty_string_option; bool new_qi = false; if (is_loclist_cmd(eap->cmdidx)) { @@ -7161,13 +7253,13 @@ void ex_helpgrep(exarg_T *eap) updated = true; } - if (p_cpo == empty_option) { + if (p_cpo == empty_string_option) { p_cpo = save_cpo; } else { // Darn, some plugin changed the value. If it's still empty it was // changed and restored, need to restore in the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err("cpo", 0L, save_cpo, 0); + set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0); } if (save_cpo_allocated) { free_string_option(save_cpo); @@ -7214,6 +7306,19 @@ void ex_helpgrep(exarg_T *eap) } } +#if defined(EXITFREE) +void free_quickfix(void) +{ + qf_free_all(NULL); + // Free all location lists + FOR_ALL_TAB_WINDOWS(tab, win) { + qf_free_all(win); + } + + ga_clear(&qfga); +} +#endif + static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) { if (what_arg->v_type == VAR_UNKNOWN) { @@ -7250,7 +7355,7 @@ void f_getqflist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) get_qf_loc_list(true, NULL, &argvars[0], rettv); } -/// Create quickfix/location list from VimL values +/// Create quickfix/location list from Vimscript values /// /// Used by `setqflist()` and `setloclist()` functions. Accepts invalid /// args argument in which case errors out, including VAR_UNKNOWN parameters. @@ -7268,7 +7373,7 @@ void f_getqflist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) FUNC_ATTR_NONNULL_ARG(2, 3) { - static char *e_invact = N_("E927: Invalid action: '%s'"); + static const char *e_invact = N_("E927: Invalid action: '%s'"); const char *title = NULL; char action = ' '; static int recursive = 0; |