From bb519fb261edf03be28f1a9150bdba58ed9d241e Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 5 Sep 2018 22:17:35 -0400 Subject: vim-patch:8.0.0733: can only add entries to one list in the quickfix stack Problem: Can only add entries to one list in the quickfix stack. Solution: Move state variables from qf_list_T to qf_list_T. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/a7df8c70c85c793bc4d75abc625d36883ab029cc --- src/nvim/quickfix.c | 218 ++++++++++++++++++++++++++-------------------------- 1 file changed, 107 insertions(+), 111 deletions(-) (limited to 'src') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index a19e98725a..dad8f52653 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -91,6 +91,14 @@ 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 + + struct dir_stack_T *qf_dir_stack; + char_u *qf_directory; + struct dir_stack_T *qf_file_stack; + char_u *qf_currfile; + bool qf_multiline; + bool qf_multiignore; + bool qf_multiscan; } qf_list_T; /// Quickfix/Location list stack definition @@ -106,15 +114,6 @@ struct qf_info_S { int qf_listcount; /* current number of lists */ int qf_curlist; /* current error list */ qf_list_T qf_lists[LISTCOUNT]; - - int qf_dir_curlist; ///< error list for qf_dir_stack - struct dir_stack_T *qf_dir_stack; - char_u *qf_directory; - struct dir_stack_T *qf_file_stack; - char_u *qf_currfile; - bool qf_multiline; - bool qf_multiignore; - bool qf_multiscan; }; static qf_info_T ql_info; /* global quickfix list */ @@ -223,9 +222,8 @@ int qf_init(win_T *wp, char_u *efile, char_u *errorformat, int newlist, qi = ll_get_or_alloc_list(wp); } - return qf_init_ext(qi, efile, curbuf, NULL, errorformat, newlist, - (linenr_T)0, (linenr_T)0, - qf_title, enc); + return qf_init_ext(qi, qi->qf_curlist, efile, curbuf, NULL, errorformat, + newlist, (linenr_T)0, (linenr_T)0, qf_title, enc); } // Maximum number of bytes allowed per line while reading an errorfile. @@ -712,8 +710,8 @@ static int qf_get_nextline(qfstate_T *state) /// Parse a line and get the quickfix fields. /// Return the QF_ status. -static int qf_parse_line(qf_info_T *qi, char_u *linebuf, size_t linelen, - efm_T *fmt_first, qffields_T *fields) +static int qf_parse_line(qf_info_T *qi, int qf_idx, char_u *linebuf, + size_t linelen, efm_T *fmt_first, qffields_T *fields) { efm_T *fmt_ptr; size_t len; @@ -721,7 +719,7 @@ static int qf_parse_line(qf_info_T *qi, char_u *linebuf, size_t linelen, int idx = 0; char_u *tail = NULL; regmatch_T regmatch; - + qf_list_T *qfl = &qi->qf_lists[qf_idx]; // Always ignore case when looking for a matching error. regmatch.rm_ic = true; @@ -740,12 +738,12 @@ static int qf_parse_line(qf_info_T *qi, char_u *linebuf, size_t linelen, restofline: for (; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) { idx = fmt_ptr->prefix; - if (qi->qf_multiscan && vim_strchr((char_u *)"OPQ", idx) == NULL) { + if (qfl->qf_multiscan && vim_strchr((char_u *)"OPQ", idx) == NULL) { continue; } fields->namebuf[0] = NUL; fields->pattern[0] = NUL; - if (!qi->qf_multiscan) { + if (!qfl->qf_multiscan) { fields->errmsg[0] = NUL; } fields->lnum = 0; @@ -759,7 +757,7 @@ restofline: int r = vim_regexec(®match, linebuf, (colnr_T)0); fmt_ptr->prog = regmatch.regprog; if (r) { - if ((idx == 'C' || idx == 'Z') && !qi->qf_multiline) { + if ((idx == 'C' || idx == 'Z') && !qfl->qf_multiline) { continue; } if (vim_strchr((char_u *)"EWI", idx) != NULL) { @@ -809,7 +807,7 @@ restofline: } fields->type = *regmatch.startp[i]; } - if (fmt_ptr->flags == '+' && !qi->qf_multiscan) { // %+ + if (fmt_ptr->flags == '+' && !qfl->qf_multiscan) { // %+ if (linelen >= fields->errmsglen) { // linelen + null terminator fields->errmsg = xrealloc(fields->errmsg, linelen + 1); @@ -877,7 +875,7 @@ restofline: break; } } - qi->qf_multiscan = false; + qfl->qf_multiscan = false; if (fmt_ptr == NULL || idx == 'D' || idx == 'X') { if (fmt_ptr != NULL) { @@ -886,13 +884,13 @@ restofline: EMSG(_("E379: Missing or empty directory name")); return QF_FAIL; } - qi->qf_directory = qf_push_dir(fields->namebuf, &qi->qf_dir_stack, - false); - if (qi->qf_directory == NULL) { + qfl->qf_directory = qf_push_dir(fields->namebuf, &qfl->qf_dir_stack, + false); + if (qfl->qf_directory == NULL) { return QF_FAIL; } } else if (idx == 'X') { // leave directory - qi->qf_directory = qf_pop_dir(&qi->qf_dir_stack); + qfl->qf_directory = qf_pop_dir(&qfl->qf_dir_stack); } } fields->namebuf[0] = NUL; // no match found, remove file name @@ -906,7 +904,7 @@ restofline: // copy whole line to error message STRLCPY(fields->errmsg, linebuf, linelen + 1); if (fmt_ptr == NULL) { - qi->qf_multiline = qi->qf_multiignore = false; + qfl->qf_multiline = qfl->qf_multiignore = false; } } else { // honor %> item @@ -915,12 +913,12 @@ restofline: } if (vim_strchr((char_u *)"AEWI", idx) != NULL) { - qi->qf_multiline = true; // start of a multi-line message - qi->qf_multiignore = false; // reset continuation + qfl->qf_multiline = true; // start of a multi-line message + qfl->qf_multiignore = false; // reset continuation } else if (vim_strchr((char_u *)"CZ", idx) != NULL) { // continuation of multi-line msg - if (!qi->qf_multiignore) { - qfline_T *qfprev = qi->qf_lists[qi->qf_curlist].qf_last; + if (!qfl->qf_multiignore) { + qfline_T *qfprev = qfl->qf_last; if (qfprev == NULL) { return QF_FAIL; } @@ -945,15 +943,15 @@ restofline: } qfprev->qf_viscol = fields->use_viscol; if (!qfprev->qf_fnum) { - qfprev->qf_fnum = qf_get_fnum(qi, qi->qf_directory, - *fields->namebuf || qi->qf_directory + qfprev->qf_fnum = qf_get_fnum(qi, qf_idx, qfl->qf_directory, + *fields->namebuf || qfl->qf_directory ? fields->namebuf - : qi->qf_currfile && fields->valid - ? qi->qf_currfile : 0); + : qfl->qf_currfile && fields->valid + ? qfl->qf_currfile : 0); } } if (idx == 'Z') { - qi->qf_multiline = qi->qf_multiignore = false; + qfl->qf_multiline = qfl->qf_multiignore = false; } line_breakcheck(); @@ -963,23 +961,23 @@ restofline: fields->valid = false; if (*fields->namebuf == NUL || os_path_exists(fields->namebuf)) { if (*fields->namebuf && idx == 'P') { - qi->qf_currfile = qf_push_dir(fields->namebuf, &qi->qf_file_stack, - true); + qfl->qf_currfile = qf_push_dir(fields->namebuf, &qfl->qf_file_stack, + true); } else if (idx == 'Q') { - qi->qf_currfile = qf_pop_dir(&qi->qf_file_stack); + qfl->qf_currfile = qf_pop_dir(&qfl->qf_file_stack); } *fields->namebuf = NUL; if (tail && *tail) { STRMOVE(IObuff, skipwhite(tail)); - qi->qf_multiscan = true; + qfl->qf_multiscan = true; goto restofline; } } } if (fmt_ptr->flags == '-') { // generally exclude this line - if (qi->qf_multiline) { + if (qfl->qf_multiline) { // also exclude continuation lines - qi->qf_multiignore = true; + qfl->qf_multiignore = true; } return QF_IGNORE_LINE; } @@ -999,6 +997,7 @@ restofline: static int qf_init_ext( qf_info_T *qi, + int qf_idx, char_u *efile, buf_T *buf, typval_T *tv, @@ -1041,17 +1040,20 @@ qf_init_ext( goto qf_init_end; } - if (newlist || qi->qf_curlist == qi->qf_listcount) { + if (newlist || qf_idx == qi->qf_listcount) { // make place for a new list qf_new_list(qi, qf_title); + qf_idx = qi->qf_curlist; } else { // Adding to existing list, use last entry. adding = true; - if (qi->qf_lists[qi->qf_curlist].qf_count > 0) { - old_last = qi->qf_lists[qi->qf_curlist].qf_last; + if (qi->qf_lists[qf_idx].qf_count > 0) { + old_last = qi->qf_lists[qf_idx].qf_last; } } + qf_list_T *qfl = &qi->qf_lists[qf_idx]; + // Use the local value of 'errorformat' if it's set. if (errorformat == p_efm && tv == NULL && buf && *buf->b_p_efm != NUL) { efm = buf->b_p_efm; @@ -1059,18 +1061,6 @@ qf_init_ext( efm = errorformat; } - // If we are not adding or adding to another list: clear the state. - if (newlist || qi->qf_curlist != qi->qf_dir_curlist) { - qi->qf_dir_curlist = qi->qf_curlist; - qf_clean_dir_stack(&qi->qf_dir_stack); - qi->qf_directory = NULL; - qf_clean_dir_stack(&qi->qf_file_stack); - qi->qf_currfile = NULL; - qi->qf_multiline = false; - qi->qf_multiignore = false; - qi->qf_multiscan = false; - } - // If the errorformat didn't change between calls, then reuse the previously // parsed values. if (last_efm == NULL || (STRCMP(last_efm, efm) != 0)) { @@ -1120,8 +1110,8 @@ qf_init_ext( break; } - status = qf_parse_line(qi, state.linebuf, state.linelen, fmt_first, - &fields); + status = qf_parse_line(qi, qf_idx, state.linebuf, state.linelen, + fmt_first, &fields); if (status == QF_FAIL) { goto error2; } @@ -1130,11 +1120,11 @@ qf_init_ext( } if (qf_add_entry(qi, - qi->qf_curlist, - qi->qf_directory, - (*fields.namebuf || qi->qf_directory) - ? fields.namebuf : ((qi->qf_currfile && fields.valid) - ? qi->qf_currfile : (char_u *)NULL), + qf_idx, + qfl->qf_directory, + (*fields.namebuf || qfl->qf_directory) + ? fields.namebuf : ((qfl->qf_currfile && fields.valid) + ? qfl->qf_currfile : (char_u *)NULL), 0, fields.errmsg, fields.lnum, @@ -1149,25 +1139,25 @@ qf_init_ext( line_breakcheck(); } if (state.fd == NULL || !ferror(state.fd)) { - if (qi->qf_lists[qi->qf_curlist].qf_index == 0) { + if (qfl->qf_index == 0) { /* no valid entry found */ - qi->qf_lists[qi->qf_curlist].qf_ptr = - qi->qf_lists[qi->qf_curlist].qf_start; - qi->qf_lists[qi->qf_curlist].qf_index = 1; - qi->qf_lists[qi->qf_curlist].qf_nonevalid = TRUE; + qfl->qf_ptr = qfl->qf_start; + qfl->qf_index = 1; + qfl->qf_nonevalid = true; } else { - qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE; - if (qi->qf_lists[qi->qf_curlist].qf_ptr == NULL) - qi->qf_lists[qi->qf_curlist].qf_ptr = - qi->qf_lists[qi->qf_curlist].qf_start; + qfl->qf_nonevalid = false; + if (qfl->qf_ptr == NULL) { + qfl->qf_ptr = qfl->qf_start; + } } /* return number of matches */ - retval = qi->qf_lists[qi->qf_curlist].qf_count; + retval = qfl->qf_count; goto qf_init_end; } EMSG(_(e_readerrf)); error2: if (!adding) { + // Error when creating a new list. Free the new list qf_free(qi, qi->qf_curlist); qi->qf_listcount--; if (qi->qf_curlist > 0) { @@ -1183,7 +1173,9 @@ qf_init_end: xfree(fields.pattern); xfree(state.growbuf); - qf_update_buffer(qi, old_last); + if (qf_idx == qi->qf_curlist) { + qf_update_buffer(qi, old_last); + } if (state.vc.vc_type != CONV_NONE) { convert_setup(&state.vc, NULL, NULL); @@ -1305,7 +1297,7 @@ static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname, (qi == &ql_info) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY; } } else { - qfp->qf_fnum = qf_get_fnum(qi, dir, fname); + qfp->qf_fnum = qf_get_fnum(qi, qf_idx, dir, fname); } qfp->qf_text = vim_strsave(mesg); qfp->qf_lnum = lnum; @@ -1488,7 +1480,8 @@ void copy_loclist(win_T *from, win_T *to) // Get buffer number for file "directory/fname". // Also sets the b_has_qf_entry flag. -static int qf_get_fnum(qf_info_T *qi, char_u *directory, char_u *fname) +static int qf_get_fnum(qf_info_T *qi, int qf_idx, char_u *directory, + char_u *fname) { char_u *ptr = NULL; char_u *bufname; @@ -1511,7 +1504,7 @@ static int qf_get_fnum(qf_info_T *qi, char_u *directory, char_u *fname) // directory change. if (!os_path_exists(ptr)) { xfree(ptr); - directory = qf_guess_filepath(qi, fname); + directory = qf_guess_filepath(qi, qf_idx, fname); if (directory) { ptr = (char_u *)concat_fnames((char *)directory, (char *)fname, true); } else { @@ -1661,18 +1654,19 @@ static void qf_clean_dir_stack(struct dir_stack_T **stackptr) * Then qf_push_dir thinks we are in ./aa/bb, but we are in ./bb. * qf_guess_filepath will return NULL. */ -static char_u *qf_guess_filepath(qf_info_T *qi, char_u *filename) +static char_u *qf_guess_filepath(qf_info_T *qi, int qf_idx, char_u *filename) { struct dir_stack_T *ds_ptr; struct dir_stack_T *ds_tmp; char_u *fullname; + qf_list_T *qfl = &qi->qf_lists[qf_idx]; // no dirs on the stack - there's nothing we can do - if (qi->qf_dir_stack == NULL) { + if (qfl->qf_dir_stack == NULL) { return NULL; } - ds_ptr = qi->qf_dir_stack->next; + ds_ptr = qfl->qf_dir_stack->next; fullname = NULL; while (ds_ptr) { xfree(fullname); @@ -1688,9 +1682,9 @@ static char_u *qf_guess_filepath(qf_info_T *qi, char_u *filename) xfree(fullname); // clean up all dirs we already left - while (qi->qf_dir_stack->next != ds_ptr) { - ds_tmp = qi->qf_dir_stack->next; - qi->qf_dir_stack->next = qi->qf_dir_stack->next->next; + while (qfl->qf_dir_stack->next != ds_ptr) { + ds_tmp = qfl->qf_dir_stack->next; + qfl->qf_dir_stack->next = qfl->qf_dir_stack->next->next; xfree(ds_tmp->dirname); xfree(ds_tmp); } @@ -2415,11 +2409,12 @@ static void qf_free_items(qf_info_T *qi, int idx) qfline_T *qfp; qfline_T *qfpnext; bool stop = false; + qf_list_T *qfl = &qi->qf_lists[idx]; - while (qi->qf_lists[idx].qf_count && qi->qf_lists[idx].qf_start != NULL) { - qfp = qi->qf_lists[idx].qf_start; + while (qfl->qf_count && qfl->qf_start != NULL) { + qfp = qfl->qf_start; qfpnext = qfp->qf_next; - if (qi->qf_lists[idx].qf_title != NULL && !stop) { + if (qfl->qf_title != NULL && !stop) { xfree(qfp->qf_text); stop = (qfp == qfpnext); xfree(qfp->qf_pattern); @@ -2428,40 +2423,41 @@ static void qf_free_items(qf_info_T *qi, int idx) // Somehow qf_count may have an incorrect value, set it to 1 // to avoid crashing when it's wrong. // TODO(vim): Avoid qf_count being incorrect. - qi->qf_lists[idx].qf_count = 1; + qfl->qf_count = 1; } } - qi->qf_lists[idx].qf_start = qfpnext; - qi->qf_lists[idx].qf_count--; - } - - qi->qf_lists[idx].qf_start = NULL; - qi->qf_lists[idx].qf_ptr = NULL; - qi->qf_lists[idx].qf_index = 0; - qi->qf_lists[idx].qf_start = NULL; - qi->qf_lists[idx].qf_last = NULL; - qi->qf_lists[idx].qf_ptr = NULL; - qi->qf_lists[idx].qf_nonevalid = true; - - qf_clean_dir_stack(&qi->qf_dir_stack); - qi->qf_directory = NULL; - qf_clean_dir_stack(&qi->qf_file_stack); - qi->qf_currfile = NULL; - qi->qf_multiline = false; - qi->qf_multiignore = false; - qi->qf_multiscan = false; + qfl->qf_start = qfpnext; + qfl->qf_count--; + } + + qfl->qf_start = NULL; + qfl->qf_ptr = NULL; + qfl->qf_index = 0; + qfl->qf_start = NULL; + qfl->qf_last = NULL; + qfl->qf_ptr = NULL; + qfl->qf_nonevalid = true; + + qf_clean_dir_stack(&qfl->qf_dir_stack); + qfl->qf_directory = NULL; + qf_clean_dir_stack(&qfl->qf_file_stack); + qfl->qf_currfile = NULL; + qfl->qf_multiline = false; + qfl->qf_multiignore = false; + qfl->qf_multiscan = false; } /// Free error list "idx". Frees all the entries in the quickfix list, /// associated context information and the title. static void qf_free(qf_info_T *qi, int idx) { + qf_list_T *qfl = &qi->qf_lists[idx]; qf_free_items(qi, idx); - xfree(qi->qf_lists[idx].qf_title); - qi->qf_lists[idx].qf_title = NULL; - tv_free(qi->qf_lists[idx].qf_ctx); - qi->qf_lists[idx].qf_ctx = NULL; + xfree(qfl->qf_title); + qfl->qf_title = NULL; + tv_free(qfl->qf_ctx); + qfl->qf_ctx = NULL; } /* @@ -4603,7 +4599,7 @@ void ex_cbuffer(exarg_T *eap) qf_title = IObuff; } - if (qf_init_ext(qi, NULL, buf, NULL, p_efm, + if (qf_init_ext(qi, qi->qf_curlist, NULL, buf, NULL, p_efm, (eap->cmdidx != CMD_caddbuffer && eap->cmdidx != CMD_laddbuffer), eap->line1, eap->line2, qf_title, NULL) > 0) { @@ -4668,7 +4664,7 @@ void ex_cexpr(exarg_T *eap) if (eval0(eap->arg, &tv, NULL, true) != FAIL) { if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL) || tv.v_type == VAR_LIST) { - if (qf_init_ext(qi, NULL, NULL, &tv, p_efm, + if (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, *eap->cmdlinep, NULL) > 0) { -- cgit From dc15dcffadca790a7b274110c0b356dcc22dfb82 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 6 Sep 2018 05:08:15 -0400 Subject: vim-patch:8.0.0782: using freed memory in quickfix code Problem: Using freed memory in quickfix code. (Dominique Pelle) Solution: Handle a help window differently. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/d28cc3f55d4a5a980f6ac6fa682382822a223720 --- src/nvim/buffer.c | 8 +++++++- src/nvim/ex_cmds.c | 6 +++--- src/nvim/quickfix.c | 28 +++++++++++++++++++--------- src/nvim/testdir/test_quickfix.vim | 14 ++++++++++++++ src/nvim/window.c | 8 ++++---- 5 files changed, 47 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 64569c294b..9815063633 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -252,7 +252,7 @@ open_buffer ( msg_silent = old_msg_silent; // Help buffer is filtered. - if (curbuf->b_help) { + if (bt_help(curbuf)) { fix_help_buffer(); } } else if (read_stdin) { @@ -4930,6 +4930,12 @@ chk_modeline ( return retval; } +// Return true if "buf" is a help buffer. +bool bt_help(const buf_T *const buf) +{ + return buf != NULL && buf->b_help; +} + /* * Return special buffer name. * Returns NULL when the buffer has a normal file name. diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index a9e9364dc3..4d32b76eb0 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4514,7 +4514,7 @@ void ex_help(exarg_T *eap) * Re-use an existing help window or open a new one. * Always open a new one for ":tab help". */ - if (!curwin->w_buffer->b_help + if (!bt_help(curwin->w_buffer) || cmdmod.tab != 0 ) { if (cmdmod.tab != 0) { @@ -4522,7 +4522,7 @@ void ex_help(exarg_T *eap) } else { wp = NULL; FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) { - if (wp2->w_buffer != NULL && wp2->w_buffer->b_help) { + if (bt_help(wp2->w_buffer)) { wp = wp2; break; } @@ -5509,7 +5509,7 @@ static int next_sign_typenr = 1; void ex_helpclose(exarg_T *eap) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { - if (win->w_buffer->b_help) { + if (bt_help(win->w_buffer)) { win_close(win, FALSE); return; } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index dad8f52653..fb7440f7da 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1839,12 +1839,12 @@ void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit) /* * For ":helpgrep" find a help window or open one. */ - if (qf_ptr->qf_type == 1 && (!curwin->w_buffer->b_help || cmdmod.tab != 0)) { + if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer) || cmdmod.tab != 0)) { win_T *wp = NULL; if (cmdmod.tab == 0) { FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) { - if (wp2->w_buffer != NULL && wp2->w_buffer->b_help) { + if (bt_help(wp2->w_buffer)) { wp = wp2; break; } @@ -4721,16 +4721,26 @@ void ex_helpgrep(exarg_T *eap) p_cpo = empty_option; if (eap->cmdidx == CMD_lhelpgrep) { - qi = NULL; + win_T *wp = NULL; - /* Find an existing help window */ - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer != NULL && wp->w_buffer->b_help) { - qi = wp->w_llist; + // If the current window is a help window, then use it + if (bt_help(curwin->w_buffer)) { + wp = curwin; + } else { + // Find an existing help window + FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) { + if (bt_help(wp2->w_buffer)) { + wp = wp2; + break; + } } } - /* Help window not found */ + if (wp == NULL) { // Help window not found + qi = NULL; + } else { + qi = wp->w_llist; + } if (qi == NULL) { /* Allocate a new location list for help text matches */ qi = ll_new_list(); @@ -4851,7 +4861,7 @@ void ex_helpgrep(exarg_T *eap) if (eap->cmdidx == CMD_lhelpgrep) { /* If the help window is not opened or if it already points to the * correct location list, then free the new location list. */ - if (!curwin->w_buffer->b_help || curwin->w_llist == qi) { + if (!bt_help(curwin->w_buffer) || curwin->w_llist == qi) { if (new_qi) ll_free_all(&qi); } else if (curwin->w_llist == NULL) diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 33abb69ca6..ade656c45d 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -2269,3 +2269,17 @@ func Test_changedtick() call Xchangedtick_tests('c') call Xchangedtick_tests('l') endfunc + +" Open multiple help windows using ":lhelpgrep +" This test used to crash Vim +func Test_Multi_LL_Help() + new | only + lhelpgrep window + lopen + e# + lhelpgrep buffer + call assert_equal(3, winnr('$')) + call assert_true(len(getloclist(1)) != 0) + call assert_true(len(getloclist(2)) != 0) + new | only +endfunc diff --git a/src/nvim/window.c b/src/nvim/window.c index 9976ae9aff..04990fedae 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1902,7 +1902,7 @@ int win_close(win_T *win, int free_buf) /* When closing the help window, try restoring a snapshot after closing * the window. Otherwise clear the snapshot, it's now invalid. */ - if (win->w_buffer != NULL && win->w_buffer->b_help) + if (bt_help(win->w_buffer)) help_window = TRUE; else clear_snapshot(curtab, SNAP_HELP_IDX); @@ -1967,8 +1967,8 @@ int win_close(win_T *win, int free_buf) if (only_one_window() && win_valid(win) && win->w_buffer == NULL && (last_window() || curtab != prev_curtab || close_last_window_tabpage(win, free_buf, prev_curtab))) { - /* Autocommands have close all windows, quit now. Restore - * curwin->w_buffer, otherwise writing ShaDa file may fail. */ + // Autocommands have closed all windows, quit now. Restore + // curwin->w_buffer, otherwise writing ShaDa file may fail. if (curwin->w_buffer == NULL) curwin->w_buffer = curbuf; getout(0); @@ -5341,7 +5341,7 @@ bool only_one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT int count = 0; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer != NULL - && (!((wp->w_buffer->b_help && !curbuf->b_help) + && (!((bt_help(wp->w_buffer) && !bt_help(curbuf)) || wp->w_p_pvw) || wp == curwin) && wp != aucmd_win) { count++; } -- cgit From cfb2828897119df1a7d59f883d151b67609b8361 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 6 Sep 2018 20:12:15 -0400 Subject: window: refactor boolean variables in win_close() free_buf (param) and help_window (variable) are bool. --- src/nvim/buffer.c | 6 +++--- src/nvim/ex_cmds.c | 2 +- src/nvim/ex_getln.c | 2 +- src/nvim/main.c | 6 +++--- src/nvim/quickfix.c | 2 +- src/nvim/tag.c | 2 +- src/nvim/window.c | 18 ++++++++---------- 7 files changed, 18 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 9815063633..5f832bfcf3 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -842,7 +842,7 @@ void goto_buffer(exarg_T *eap, int start, int dir, int count) enter_cleanup(&cs); /* Quitting means closing the split window, nothing else. */ - win_close(curwin, TRUE); + win_close(curwin, true); swap_exists_action = SEA_NONE; swap_exists_did_quit = TRUE; @@ -4651,7 +4651,7 @@ void ex_buffer_all(exarg_T *eap) && !ONE_WINDOW && !(wp->w_closing || wp->w_buffer->b_locked > 0) ) { - win_close(wp, FALSE); + win_close(wp, false); wpnext = firstwin; /* just in case an autocommand does something strange with windows */ tpnext = first_tabpage; /* start all over...*/ @@ -4724,7 +4724,7 @@ void ex_buffer_all(exarg_T *eap) enter_cleanup(&cs); /* User selected Quit at ATTENTION prompt; close this window. */ - win_close(curwin, TRUE); + win_close(curwin, true); --open_wins; swap_exists_action = SEA_NONE; swap_exists_did_quit = TRUE; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 4d32b76eb0..ac5b3af459 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -5510,7 +5510,7 @@ void ex_helpclose(exarg_T *eap) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { if (bt_help(win->w_buffer)) { - win_close(win, FALSE); + win_close(win, false); return; } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index ff625d6dc9..31231fe725 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -6192,7 +6192,7 @@ static int open_cmdwin(void) wp = curwin; set_bufref(&bufref, curbuf); win_goto(old_curwin); - win_close(wp, TRUE); + win_close(wp, true); // win_close() may have already wiped the buffer when 'bh' is // set to 'wipe'. diff --git a/src/nvim/main.c b/src/nvim/main.c index ab8b33aa12..0698a8dc0d 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1551,7 +1551,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) /* When w_arg_idx is -1 remove the window (see create_windows()). */ if (curwin->w_arg_idx == -1) { - win_close(curwin, TRUE); + win_close(curwin, true); advance = FALSE; } @@ -1563,7 +1563,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) // When w_arg_idx is -1 remove the window (see create_windows()). if (curwin->w_arg_idx == -1) { ++arg_idx; - win_close(curwin, TRUE); + win_close(curwin, true); advance = FALSE; continue; } @@ -1598,7 +1598,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) did_emsg = FALSE; /* avoid hit-enter prompt */ getout(1); } - win_close(curwin, TRUE); + win_close(curwin, true); advance = FALSE; } if (arg_idx == GARGCOUNT - 1) diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index fb7440f7da..d719f2e0a1 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -2598,7 +2598,7 @@ void ex_cclose(exarg_T *eap) /* Find existing quickfix window and close it. */ win = qf_find_win(qi); if (win != NULL) - win_close(win, FALSE); + win_close(win, false); } /* diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 2a980af2a2..91f182dd50 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2612,7 +2612,7 @@ static int jumpto_tag( } else { --RedrawingDisabled; if (postponed_split) { /* close the window */ - win_close(curwin, FALSE); + win_close(curwin, false); postponed_split = 0; } } diff --git a/src/nvim/window.c b/src/nvim/window.c index 04990fedae..02bf10f9a7 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1859,20 +1859,18 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, return true; } -/* - * Close window "win". Only works for the current tab page. - * If "free_buf" is TRUE related buffer may be unloaded. - * - * Called by :quit, :close, :xit, :wq and findtag(). - * Returns FAIL when the window was not closed. - */ -int win_close(win_T *win, int free_buf) +// Close window "win". Only works for the current tab page. +// If "free_buf" is true related buffer may be unloaded. +// +// Called by :quit, :close, :xit, :wq and findtag(). +// Returns FAIL when the window was not closed. +int win_close(win_T *win, bool free_buf) { win_T *wp; int other_buffer = FALSE; int close_curwin = FALSE; int dir; - int help_window = FALSE; + bool help_window = false; tabpage_T *prev_curtab = curtab; frame_T *win_frame = win->w_frame->fr_parent; @@ -1903,7 +1901,7 @@ int win_close(win_T *win, int free_buf) /* When closing the help window, try restoring a snapshot after closing * the window. Otherwise clear the snapshot, it's now invalid. */ if (bt_help(win->w_buffer)) - help_window = TRUE; + help_window = true; else clear_snapshot(curtab, SNAP_HELP_IDX); -- cgit From c0d26ba4f9a5a531e062fdfe96dddaeed56cdcc2 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 6 Sep 2018 20:18:12 -0400 Subject: main: advance in edit_buffers() is bool --- src/nvim/main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/main.c b/src/nvim/main.c index 0698a8dc0d..fbcfe9a3ce 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1540,7 +1540,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) { int arg_idx; /* index in argument list */ int i; - int advance = TRUE; + bool advance = true; win_T *win; /* @@ -1552,7 +1552,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) /* When w_arg_idx is -1 remove the window (see create_windows()). */ if (curwin->w_arg_idx == -1) { win_close(curwin, true); - advance = FALSE; + advance = false; } arg_idx = 1; @@ -1564,7 +1564,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) if (curwin->w_arg_idx == -1) { ++arg_idx; win_close(curwin, true); - advance = FALSE; + advance = false; continue; } @@ -1579,7 +1579,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) win_enter(curwin->w_next, false); } } - advance = TRUE; + advance = true; // Only open the file if there is no file in this window yet (that can // happen when vimrc contains ":sall"). @@ -1599,7 +1599,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) getout(1); } win_close(curwin, true); - advance = FALSE; + advance = false; } if (arg_idx == GARGCOUNT - 1) arg_had_last = TRUE; -- cgit From 2be853d486be2cac3b04efbf425afac2dd9361a3 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 5 Sep 2018 23:04:54 -0400 Subject: lint --- src/nvim/buffer.c | 12 ++++++------ src/nvim/main.c | 2 +- src/nvim/quickfix.c | 13 ++++++++----- src/nvim/tag.c | 6 +++--- src/nvim/window.c | 8 +++++--- 5 files changed, 23 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 5f832bfcf3..384c7f77b2 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -841,7 +841,7 @@ void goto_buffer(exarg_T *eap, int start, int dir, int count) * aborting() returns FALSE when closing a window. */ enter_cleanup(&cs); - /* Quitting means closing the split window, nothing else. */ + // Quitting means closing the split window, nothing else. win_close(curwin, true); swap_exists_action = SEA_NONE; swap_exists_did_quit = TRUE; @@ -4652,9 +4652,9 @@ void ex_buffer_all(exarg_T *eap) && !(wp->w_closing || wp->w_buffer->b_locked > 0) ) { win_close(wp, false); - wpnext = firstwin; /* just in case an autocommand does - something strange with windows */ - tpnext = first_tabpage; /* start all over...*/ + wpnext = firstwin; // just in case an autocommand does + // something strange with windows + tpnext = first_tabpage; // start all over... open_wins = 0; } else ++open_wins; @@ -4723,9 +4723,9 @@ void ex_buffer_all(exarg_T *eap) * aborting() returns FALSE when closing a window. */ enter_cleanup(&cs); - /* User selected Quit at ATTENTION prompt; close this window. */ + // User selected Quit at ATTENTION prompt; close this window. win_close(curwin, true); - --open_wins; + open_wins--; swap_exists_action = SEA_NONE; swap_exists_did_quit = TRUE; diff --git a/src/nvim/main.c b/src/nvim/main.c index fbcfe9a3ce..d5e37929b9 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1562,7 +1562,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) } // When w_arg_idx is -1 remove the window (see create_windows()). if (curwin->w_arg_idx == -1) { - ++arg_idx; + arg_idx++; win_close(curwin, true); advance = false; continue; diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d719f2e0a1..1eb6b3b421 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1140,7 +1140,7 @@ qf_init_ext( } if (state.fd == NULL || !ferror(state.fd)) { if (qfl->qf_index == 0) { - /* no valid entry found */ + // no valid entry found qfl->qf_ptr = qfl->qf_start; qfl->qf_index = 1; qfl->qf_nonevalid = true; @@ -1150,7 +1150,7 @@ qf_init_ext( qfl->qf_ptr = qfl->qf_start; } } - /* return number of matches */ + // return number of matches retval = qfl->qf_count; goto qf_init_end; } @@ -2597,8 +2597,9 @@ void ex_cclose(exarg_T *eap) /* Find existing quickfix window and close it. */ win = qf_find_win(qi); - if (win != NULL) + if (win != NULL) { win_close(win, false); + } } /* @@ -4862,10 +4863,12 @@ void ex_helpgrep(exarg_T *eap) /* If the help window is not opened or if it already points to the * correct location list, then free the new location list. */ if (!bt_help(curwin->w_buffer) || curwin->w_llist == qi) { - if (new_qi) + if (new_qi) { ll_free_all(&qi); - } else if (curwin->w_llist == NULL) + } + } else if (curwin->w_llist == NULL) { curwin->w_llist = qi; + } } } diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 91f182dd50..54090afd71 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2608,10 +2608,10 @@ static int jumpto_tag( win_enter(curwin_save, true); } - --RedrawingDisabled; + RedrawingDisabled--; } else { - --RedrawingDisabled; - if (postponed_split) { /* close the window */ + RedrawingDisabled--; + if (postponed_split) { // close the window win_close(curwin, false); postponed_split = 0; } diff --git a/src/nvim/window.c b/src/nvim/window.c index 02bf10f9a7..6f382acd84 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1900,10 +1900,11 @@ int win_close(win_T *win, bool free_buf) /* When closing the help window, try restoring a snapshot after closing * the window. Otherwise clear the snapshot, it's now invalid. */ - if (bt_help(win->w_buffer)) + if (bt_help(win->w_buffer)) { help_window = true; - else + } else { clear_snapshot(curtab, SNAP_HELP_IDX); + } if (win == curwin) { /* @@ -1967,8 +1968,9 @@ int win_close(win_T *win, bool free_buf) || close_last_window_tabpage(win, free_buf, prev_curtab))) { // Autocommands have closed all windows, quit now. Restore // curwin->w_buffer, otherwise writing ShaDa file may fail. - if (curwin->w_buffer == NULL) + if (curwin->w_buffer == NULL) { curwin->w_buffer = curbuf; + } getout(0); } // Autocommands may have moved to another tab page. -- cgit From 4eb923bfe0658a03c53326ecc7fea4ca2b820e34 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 6 Sep 2018 21:54:30 -0400 Subject: vim-patch:8.0.0904: cannot set a location list from text Problem: Cannot set a location list from text. Solution: Add the "text" argument to setqflist(). (Yegappan Lakshmanan) https://github.com/vim/vim/commit/ae338338508ef42866204f90dca861ac555f4298 --- src/nvim/quickfix.c | 25 ++++++- src/nvim/testdir/test_quickfix.vim | 136 ++++++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 1eb6b3b421..fb60b60605 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -4330,7 +4330,8 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, return retval; } -static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) +static int qf_set_properties(qf_info_T *qi, dict_T *what, int action, + char_u *title) { dictitem_T *di; int retval = FAIL; @@ -4368,7 +4369,7 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) } if (newlist) { - qf_new_list(qi, NULL); + qf_new_list(qi, title); qf_idx = qi->qf_curlist; } @@ -4393,6 +4394,24 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) } } + if ((di = tv_dict_find(what, S_LEN("text"))) != NULL) { + // Only string and list values are supported + if ((di->di_tv.v_type == VAR_STRING + && di->di_tv.vval.v_string != NULL) + || (di->di_tv.v_type == VAR_LIST + && di->di_tv.vval.v_list != NULL)) { + if (action == 'r') { + qf_free_items(qi, qf_idx); + } + if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, p_efm, + false, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) { + retval = OK; + } + } else { + return FAIL; + } + } + if ((di = tv_dict_find(what, S_LEN("context"))) != NULL) { tv_free(qi->qf_lists[qf_idx].qf_ctx); @@ -4480,7 +4499,7 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title, // Free the entire quickfix or location list stack qf_free_stack(wp, qi); } else if (what != NULL) { - retval = qf_set_properties(qi, what, action); + retval = qf_set_properties(qi, what, action, title); } else { retval = qf_add_entries(qi, qi->qf_curlist, list, title, action); } diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index ade656c45d..6340721683 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1844,6 +1844,11 @@ func Xproperty_tests(cchar) let l = g:Xgetlist({'items':1}) call assert_equal(0, len(l.items)) + " The following used to crash Vim with address sanitizer + call g:Xsetlist([], 'f') + call g:Xsetlist([], 'a', {'items' : [{'filename':'F1', 'lnum':10}]}) + call assert_equal(10, g:Xgetlist({'items':1}).items[0].lnum) + " Save and restore the quickfix stack call g:Xsetlist([], 'f') call assert_equal(0, g:Xgetlist({'nr':'$'}).nr) @@ -2266,8 +2271,135 @@ func Xchangedtick_tests(cchar) endfunc func Test_changedtick() - call Xchangedtick_tests('c') - call Xchangedtick_tests('l') + call Xchangedtick_tests('c') + call Xchangedtick_tests('l') +endfunc + +" Tests for parsing an expression using setqflist() +func Xsetexpr_tests(cchar) + call s:setup_commands(a:cchar) + + let t = ["File1:10:Line10", "File1:20:Line20"] + call g:Xsetlist([], ' ', {'text' : t}) + call g:Xsetlist([], 'a', {'text' : "File1:30:Line30"}) + + let l = g:Xgetlist() + call assert_equal(3, len(l)) + call assert_equal(20, l[1].lnum) + call assert_equal('Line30', l[2].text) + call g:Xsetlist([], 'r', {'text' : "File2:5:Line5"}) + let l = g:Xgetlist() + call assert_equal(1, len(l)) + call assert_equal('Line5', l[0].text) + call assert_equal(-1, g:Xsetlist([], 'a', {'text' : 10})) + + call g:Xsetlist([], 'f') + " Add entries to multiple lists + call g:Xsetlist([], 'a', {'nr' : 1, 'text' : ["File1:10:Line10"]}) + call g:Xsetlist([], 'a', {'nr' : 2, 'text' : ["File2:20:Line20"]}) + call g:Xsetlist([], 'a', {'nr' : 1, 'text' : ["File1:15:Line15"]}) + call g:Xsetlist([], 'a', {'nr' : 2, 'text' : ["File2:25:Line25"]}) + call assert_equal('Line15', g:Xgetlist({'nr':1, 'items':1}).items[1].text) + call assert_equal('Line25', g:Xgetlist({'nr':2, 'items':1}).items[1].text) +endfunc + +func Test_setexpr() + call Xsetexpr_tests('c') + call Xsetexpr_tests('l') +endfunc + +" Tests for per quickfix/location list directory stack +func Xmultidirstack_tests(cchar) + call s:setup_commands(a:cchar) + + call g:Xsetlist([], 'f') + Xexpr "" | Xexpr "" + + call g:Xsetlist([], 'a', {'nr' : 1, 'text' : "Entering dir 'Xone/a'"}) + call g:Xsetlist([], 'a', {'nr' : 2, 'text' : "Entering dir 'Xtwo/a'"}) + call g:Xsetlist([], 'a', {'nr' : 1, 'text' : "one.txt:3:one one one"}) + call g:Xsetlist([], 'a', {'nr' : 2, 'text' : "two.txt:5:two two two"}) + + let l1 = g:Xgetlist({'nr':1, 'items':1}) + let l2 = g:Xgetlist({'nr':2, 'items':1}) + call assert_equal('Xone/a/one.txt', bufname(l1.items[1].bufnr)) + call assert_equal(3, l1.items[1].lnum) + call assert_equal('Xtwo/a/two.txt', bufname(l2.items[1].bufnr)) + call assert_equal(5, l2.items[1].lnum) +endfunc + +func Test_multidirstack() + call mkdir('Xone/a', 'p') + call mkdir('Xtwo/a', 'p') + let lines = ['1', '2', 'one one one', '4', 'two two two', '6', '7'] + call writefile(lines, 'Xone/a/one.txt') + call writefile(lines, 'Xtwo/a/two.txt') + let save_efm = &efm + set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f' + + call Xmultidirstack_tests('c') + call Xmultidirstack_tests('l') + + let &efm = save_efm + call delete('Xone', 'rf') + call delete('Xtwo', 'rf') +endfunc + +" Tests for per quickfix/location list file stack +func Xmultifilestack_tests(cchar) + call s:setup_commands(a:cchar) + + call g:Xsetlist([], 'f') + Xexpr "" | Xexpr "" + + call g:Xsetlist([], 'a', {'nr' : 1, 'text' : "[one.txt]"}) + call g:Xsetlist([], 'a', {'nr' : 2, 'text' : "[two.txt]"}) + call g:Xsetlist([], 'a', {'nr' : 1, 'text' : "(3,5) one one one"}) + call g:Xsetlist([], 'a', {'nr' : 2, 'text' : "(5,9) two two two"}) + + let l1 = g:Xgetlist({'nr':1, 'items':1}) + let l2 = g:Xgetlist({'nr':2, 'items':1}) + call assert_equal('one.txt', bufname(l1.items[1].bufnr)) + call assert_equal(3, l1.items[1].lnum) + call assert_equal('two.txt', bufname(l2.items[1].bufnr)) + call assert_equal(5, l2.items[1].lnum) +endfunc + +func Test_multifilestack() + let lines = ['1', '2', 'one one one', '4', 'two two two', '6', '7'] + call writefile(lines, 'one.txt') + call writefile(lines, 'two.txt') + let save_efm = &efm + set efm=%+P[%f],(%l\\,%c)\ %m,%-Q + + call Xmultifilestack_tests('c') + call Xmultifilestack_tests('l') + + let &efm = save_efm + call delete('one.txt') + call delete('two.txt') +endfunc + +" Tests for per buffer 'efm' setting +func Test_perbuf_efm() + call writefile(["File1-10-Line10"], 'one.txt') + call writefile(["File2#20#Line20"], 'two.txt') + set efm=%f#%l#%m + new | only + new + setlocal efm=%f-%l-%m + cfile one.txt + wincmd w + caddfile two.txt + + let l = getqflist() + call assert_equal(10, l[0].lnum) + call assert_equal('Line20', l[1].text) + + set efm& + new | only + call delete('one.txt') + call delete('two.txt') endfunc " Open multiple help windows using ":lhelpgrep -- cgit From d99a2689143827fae19e2354d27287587f2059cb Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 6 Sep 2018 23:49:21 -0400 Subject: vim-patch:8.0.0922: quickfix list always added after current one Problem: Quickfix list always added after current one. Solution: Make it possible to add a quickfix list after the last one. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/55b6926450d75788dada3ff44a35e328224df758 --- src/nvim/quickfix.c | 25 ++++++---- src/nvim/testdir/test_quickfix.vim | 98 +++++++++++++++++++++++++++++++++++--- 2 files changed, 108 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index fb60b60605..263b8b3a77 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1198,9 +1198,9 @@ static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title) } } -/* - * Prepare for adding a new quickfix list. - */ +// Prepare for adding a new quickfix list. If the current list is in the +// middle of the stack, then all the following lists are freed and then +// the new list is added. static void qf_new_list(qf_info_T *qi, char_u *qf_title) { int i; @@ -4351,24 +4351,31 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action, if ((action == ' ' || action == 'a') && qf_idx == qi->qf_listcount) { // When creating a new list, accept qf_idx pointing to the next - // non-available list + // non-available list and add the new list at the end of the + // stack. newlist = true; + qf_idx = qi->qf_listcount - 1; } else if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { return FAIL; - } else { + } 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, "$") - && qi->qf_listcount > 0) { - qf_idx = qi->qf_listcount - 1; - newlist = false; + && strequal((const char *)di->di_tv.vval.v_string, "$")) { + if (qi->qf_listcount > 0) { + qf_idx = qi->qf_listcount - 1; + } else if (newlist) { + qf_idx = 0; + } else { + return FAIL; + } } else { return FAIL; } } if (newlist) { + qi->qf_curlist = qf_idx; qf_new_list(qi, title); qf_idx = qi->qf_curlist; } diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 6340721683..f603f46d63 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -11,7 +11,7 @@ func s:setup_commands(cchar) command! -nargs=* -bang Xlist clist command! -nargs=* Xgetexpr cgetexpr command! -nargs=* Xaddexpr caddexpr - command! -nargs=* Xolder colder + command! -nargs=* -count Xolder colder command! -nargs=* Xnewer cnewer command! -nargs=* Xopen copen command! -nargs=* Xwindow cwindow @@ -43,7 +43,7 @@ func s:setup_commands(cchar) command! -nargs=* -bang Xlist llist command! -nargs=* Xgetexpr lgetexpr command! -nargs=* Xaddexpr laddexpr - command! -nargs=* Xolder lolder + command! -nargs=* -count Xolder lolder command! -nargs=* Xnewer lnewer command! -nargs=* Xopen lopen command! -nargs=* Xwindow lwindow @@ -1727,7 +1727,7 @@ func Xproperty_tests(cchar) call assert_equal('N2', g:Xgetlist({'nr':2, 'title':1}).title) " Changing the title of an earlier quickfix list - call g:Xsetlist([], ' ', {'title' : 'NewTitle', 'nr' : 2}) + call g:Xsetlist([], 'r', {'title' : 'NewTitle', 'nr' : 2}) call assert_equal('NewTitle', g:Xgetlist({'nr':2, 'title':1}).title) " Changing the title of an invalid quickfix list @@ -1794,10 +1794,10 @@ func Xproperty_tests(cchar) Xexpr "One" Xexpr "Two" Xexpr "Three" - call g:Xsetlist([], ' ', {'context' : [1], 'nr' : 1}) - call g:Xsetlist([], ' ', {'context' : [2], 'nr' : 2}) + call g:Xsetlist([], 'r', {'context' : [1], 'nr' : 1}) + call g:Xsetlist([], 'a', {'context' : [2], 'nr' : 2}) " Also, check for setting the context using quickfix list number zero. - call g:Xsetlist([], ' ', {'context' : [3], 'nr' : 0}) + call g:Xsetlist([], 'r', {'context' : [3], 'nr' : 0}) call test_garbagecollect_now() let l = g:Xgetlist({'nr' : 1, 'context' : 1}) call assert_equal([1], l.context) @@ -2415,3 +2415,89 @@ func Test_Multi_LL_Help() call assert_true(len(getloclist(2)) != 0) new | only endfunc + +" Tests for adding new quickfix lists using setqflist() +func XaddQf_tests(cchar) + call s:setup_commands(a:cchar) + + " Create a new list using ' ' for action + call g:Xsetlist([], 'f') + call g:Xsetlist([], ' ', {'title' : 'Test1'}) + let l = g:Xgetlist({'nr' : '$', 'all' : 1}) + call assert_equal(1, l.nr) + call assert_equal('Test1', l.title) + + " Create a new list using ' ' for action and '$' for 'nr' + call g:Xsetlist([], 'f') + call g:Xsetlist([], ' ', {'title' : 'Test2', 'nr' : '$'}) + let l = g:Xgetlist({'nr' : '$', 'all' : 1}) + call assert_equal(1, l.nr) + call assert_equal('Test2', l.title) + + " Create a new list using 'a' for action + call g:Xsetlist([], 'f') + call g:Xsetlist([], 'a', {'title' : 'Test3'}) + let l = g:Xgetlist({'nr' : '$', 'all' : 1}) + call assert_equal(1, l.nr) + call assert_equal('Test3', l.title) + + " Create a new list using 'a' for action and '$' for 'nr' + call g:Xsetlist([], 'f') + call g:Xsetlist([], 'a', {'title' : 'Test3', 'nr' : '$'}) + call g:Xsetlist([], 'a', {'title' : 'Test4'}) + let l = g:Xgetlist({'nr' : '$', 'all' : 1}) + call assert_equal(1, l.nr) + call assert_equal('Test4', l.title) + + " Adding a quickfix list should remove all the lists following the current + " list. + Xexpr "" | Xexpr "" | Xexpr "" + silent! 10Xolder + call g:Xsetlist([], ' ', {'title' : 'Test5'}) + let l = g:Xgetlist({'nr' : '$', 'all' : 1}) + call assert_equal(2, l.nr) + call assert_equal('Test5', l.title) + + " Add a quickfix list using '$' as the list number. + let lastqf = g:Xgetlist({'nr':'$'}).nr + silent! 99Xolder + call g:Xsetlist([], ' ', {'nr' : '$', 'title' : 'Test6'}) + let l = g:Xgetlist({'nr' : '$', 'all' : 1}) + call assert_equal(lastqf + 1, l.nr) + call assert_equal('Test6', l.title) + + " Add a quickfix list using 'nr' set to one more than the quickfix + " list size. + let lastqf = g:Xgetlist({'nr':'$'}).nr + silent! 99Xolder + call g:Xsetlist([], ' ', {'nr' : lastqf + 1, 'title' : 'Test7'}) + let l = g:Xgetlist({'nr' : '$', 'all' : 1}) + call assert_equal(lastqf + 1, l.nr) + call assert_equal('Test7', l.title) + + " Add a quickfix list to a stack with 10 lists using 'nr' set to '$' + exe repeat('Xexpr "" |', 9) . 'Xexpr ""' + silent! 99Xolder + call g:Xsetlist([], ' ', {'nr' : '$', 'title' : 'Test8'}) + let l = g:Xgetlist({'nr' : '$', 'all' : 1}) + call assert_equal(10, l.nr) + call assert_equal('Test8', l.title) + + " Add a quickfix list using 'nr' set to a value greater than 10 + call assert_equal(-1, g:Xsetlist([], ' ', {'nr' : 12, 'title' : 'Test9'})) + + " Try adding a quickfix list with 'nr' set to a value greater than the + " quickfix list size but less than 10. + call g:Xsetlist([], 'f') + Xexpr "" | Xexpr "" | Xexpr "" + silent! 99Xolder + call assert_equal(-1, g:Xsetlist([], ' ', {'nr' : 8, 'title' : 'Test10'})) + + " Add a quickfix list using 'nr' set to a some string or list + call assert_equal(-1, g:Xsetlist([], ' ', {'nr' : [1,2], 'title' : 'Test11'})) +endfunc + +func Test_add_qf() + call XaddQf_tests('c') + call XaddQf_tests('l') +endfunc -- cgit