diff options
-rw-r--r-- | runtime/doc/index.txt | 2 | ||||
-rw-r--r-- | runtime/doc/quickfix.txt | 13 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 11 | ||||
-rw-r--r-- | src/nvim/eval.c | 10 | ||||
-rw-r--r-- | src/nvim/ex_cmds.lua | 12 | ||||
-rw-r--r-- | src/nvim/po/uk.po | 4 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 1363 | ||||
-rw-r--r-- | src/nvim/testdir/Makefile | 5 | ||||
-rw-r--r-- | src/nvim/testdir/samples/quickfix.txt | 4 | ||||
-rw-r--r-- | src/nvim/testdir/test_quickfix.vim | 1422 | ||||
-rw-r--r-- | src/nvim/version.c | 46 | ||||
-rw-r--r-- | test/functional/legacy/quickfix_spec.lua | 683 | ||||
-rw-r--r-- | test/functional/viml/errorlist_spec.lua | 12 |
13 files changed, 2332 insertions, 1255 deletions
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index bd2df5d1e5..1f4557fe30 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1129,6 +1129,7 @@ tag command action ~ |:caddfile| :caddf[ile] add error message to current quickfix list |:call| :cal[l] call a function |:catch| :cat[ch] part of a :try command +|:cbottom| :cbo[ttom] scroll to the bottom of the quickfix window |:cbuffer| :cb[uffer] parse error messages and jump to first error |:cc| :cc go to specific error |:cclose| :ccl[ose] close quickfix window @@ -1286,6 +1287,7 @@ tag command action ~ |:last| :la[st] go to the last file in the argument list |:language| :lan[guage] set the language (locale) |:later| :lat[er] go to newer change, redo +|:lbottom| :lbo[ttom] scroll to the bottom of the location window |:lbuffer| :lb[uffer] parse locations and jump to first location |:lcd| :lc[d] change directory locally |:lchdir| :lch[dir] change directory locally diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 7c1fc30eba..cbb2a23a48 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -1,4 +1,4 @@ -*quickfix.txt* For Vim version 7.4. Last change: 2016 Jul 01 +*quickfix.txt* For Vim version 7.4. Last change: 2016 Jul 07 VIM REFERENCE MANUAL by Bram Moolenaar @@ -428,6 +428,17 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST: :lw[indow] [height] Same as ":cwindow", except use the window showing the location list for the current window. + *:cbo* *:cbottom* +:cbo[ttom] Put the cursor in the last line of the quickfix window + and scroll to make it visible. This is useful for + when errors are added by an asynchronous callback. + Only call it once in a while if there are many + updates to avoid a lot of redrawing. + + *:lbo* *:lbottom* +:lbo[ttom] Same as ":cbottom", except use the window showing the + location list for the current window. + Normally the quickfix window is at the bottom of the screen. If there are vertical splits, it's at the bottom of the rightmost column of windows. To make it always occupy the full width: > diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 2f0e8ad974..0418a737eb 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -609,6 +609,7 @@ struct file_buffer { int b_p_bomb; ///< 'bomb' char_u *b_p_bh; ///< 'bufhidden' char_u *b_p_bt; ///< 'buftype' + bool b_has_qf_entry; ///< quickfix exists for buffer int b_p_bl; ///< 'buflisted' int b_p_cin; ///< 'cindent' char_u *b_p_cino; ///< 'cinoptions' @@ -1036,11 +1037,11 @@ struct window_S { */ int w_wrow, w_wcol; /* cursor position in window */ - linenr_T w_botline; /* number of the line below the bottom of - the screen */ - int w_empty_rows; /* number of ~ rows in window */ - int w_filler_rows; /* number of filler rows at the end of the - window */ + linenr_T w_botline; // number of the line below the bottom of + // the window + int w_empty_rows; // number of ~ rows in window + int w_filler_rows; // number of filler rows at the end of the + // window /* * Info about the lines currently in the window is remembered to avoid diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 39e121eb53..bdbd77337d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -158,7 +158,7 @@ static char *e_listdictarg = N_( static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary"); static char *e_listreq = N_("E714: List required"); static char *e_dictreq = N_("E715: Dictionary required"); -static char *e_strreq = N_("E114: String required"); +static char *e_stringreq = N_("E928: String required"); static char *e_toomanyarg = N_("E118: Too many arguments for function: %s"); static char *e_dictkey = N_("E716: Key not present in Dictionary: %s"); static char *e_funcexts = N_( @@ -14996,6 +14996,7 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr 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'"); char_u *title = NULL; int action = ' '; rettv->vval.v_number = -1; @@ -15011,12 +15012,15 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) // Option argument was not given. goto skip_args; } else if (action_arg->v_type != VAR_STRING) { - EMSG(_(e_strreq)); + EMSG(_(e_stringreq)); return; } char_u *act = get_tv_string_chk(action_arg); - if (*act == 'a' || *act == 'r') { + if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) { action = *act; + } else { + EMSG2(_(e_invact), act); + return; } typval_T *title_arg = &args[2]; diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index b056fff667..b998b81284 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -358,6 +358,12 @@ return { func='ex_cbuffer', }, { + command='cbottom', + flags=bit.bor(TRLBAR), + addr_type=ADDR_LINES, + func='ex_cbottom', + }, + { command='cc', flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), addr_type=ADDR_LINES, @@ -1272,6 +1278,12 @@ return { func='ex_cbuffer', }, { + command='lbottom', + flags=bit.bor(TRLBAR), + addr_type=ADDR_LINES, + func='ex_cbottom', + }, + { command='lcd', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), addr_type=ADDR_LINES, diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po index bbbb462292..3145931bfe 100644 --- a/src/nvim/po/uk.po +++ b/src/nvim/po/uk.po @@ -564,8 +564,8 @@ msgstr "E714: Потрібен список" msgid "E715: Dictionary required" msgstr "E715: Потрібен словник" -msgid "E114: String required" -msgstr "E114: Потрібен String" +msgid "E928: String required" +msgstr "E928: Потрібен String" #, c-format msgid "E118: Too many arguments for function: %s" diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index f0d77f9e2b..33a84660c1 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -48,8 +48,6 @@ struct dir_stack_T { char_u *dirname; }; -static struct dir_stack_T *dir_stack = NULL; - /* * For each error the next struct is allocated and linked in a list. */ @@ -76,13 +74,14 @@ struct qfline_S { #define LISTCOUNT 10 typedef struct qf_list_S { - qfline_T *qf_start; /* pointer to the first error */ - qfline_T *qf_ptr; /* pointer to the current error */ - int qf_count; /* number of errors (0 means no error list) */ - int qf_index; /* current index in the error list */ - int qf_nonevalid; /* TRUE if not a single valid entry found */ - char_u *qf_title; /* title derived from the command that created - * the error list */ + 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 no error list) + int qf_index; // current index in the error list + int qf_nonevalid; // TRUE if not a single valid entry found + char_u *qf_title; // title derived from the command that created + // the error list } qf_list_T; struct qf_info_S { @@ -96,6 +95,15 @@ 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 */ @@ -168,17 +176,268 @@ qf_init ( qf_title); } -/* - * Read the errorfile "efile" into memory, line by line, building the error - * list. - * Alternative: when "efile" is null read errors from buffer "buf". - * Always use 'errorformat' from "buf" if there is a local value. - * Then "lnumfirst" and "lnumlast" specify the range of lines to use. - * Set the title of the list to "qf_title". - * Return -1 for error, number of errors for success. - */ -static int -qf_init_ext ( +// Maximum number of bytes allowed per line while reading an errorfile. +static const size_t LINE_MAXLEN = 4096; + +static char_u *qf_grow_linebuf(char_u **growbuf, size_t *growbufsiz, + size_t newsz, size_t *allocsz) +{ + // If the line exceeds LINE_MAXLEN exclude the last + // byte since it's not a NL character. + *allocsz = newsz > LINE_MAXLEN ? LINE_MAXLEN - 1 : newsz; + if (*growbuf == NULL) { + *growbuf = xmalloc(*allocsz + 1); + *growbufsiz = *allocsz; + } else if (*allocsz > *growbufsiz) { + *growbuf = xrealloc(*growbuf, *allocsz + 1); + *growbufsiz = *allocsz; + } + return *growbuf; +} + +static struct fmtpattern +{ + char_u convchar; + char *pattern; +} fmt_pat[FMT_PATTERNS] = +{ + { 'f', ".\\+" }, // only used when at end + { 'n', "\\d\\+" }, + { 'l', "\\d\\+" }, + { 'c', "\\d\\+" }, + { 't', "." }, + { 'm', ".\\+" }, + { 'r', ".*" }, + { 'p', "[- .]*" }, // NOLINT(whitespace/tab) + { 'v', "\\d\\+" }, + { 's', ".\\+" } +}; + +// Converts a 'errorformat' string to regular expression pattern +static int efm_to_regpat(char_u *efm, int len, efm_T *fmt_ptr, + char_u *regpat, char_u *errmsg) +{ + // Build regexp pattern from current 'errorformat' option + char_u *ptr = regpat; + *ptr++ = '^'; + int round = 0; + for (char_u *efmp = efm; efmp < efm + len; efmp++) { + if (*efmp == '%') { + efmp++; + int idx; + for (idx = 0; idx < FMT_PATTERNS; idx++) { + if (fmt_pat[idx].convchar == *efmp) { + break; + } + } + if (idx < FMT_PATTERNS) { + if (fmt_ptr->addr[idx]) { + snprintf((char *)errmsg, CMDBUFFSIZE + 1, + _("E372: Too many %%%c in format string"), *efmp); + EMSG(errmsg); + return -1; + } + if ((idx + && idx < 6 + && vim_strchr((char_u *)"DXOPQ", fmt_ptr->prefix) != NULL) + || (idx == 6 + && vim_strchr((char_u *)"OPQ", fmt_ptr->prefix) == NULL)) { + snprintf((char *)errmsg, CMDBUFFSIZE + 1, + _("E373: Unexpected %%%c in format string"), *efmp); + EMSG(errmsg); + return -1; + } + round++; + fmt_ptr->addr[idx] = (char_u)round; + *ptr++ = '\\'; + *ptr++ = '('; +#ifdef BACKSLASH_IN_FILENAME + if (*efmp == 'f') { + // Also match "c:" in the file name, even when + // checking for a colon next: "%f:". + // "\%(\a:\)\=" + STRCPY(ptr, "\\%(\\a:\\)\\="); + ptr += 10; + } +#endif + if (*efmp == 'f' && efmp[1] != NUL) { + if (efmp[1] != '\\' && efmp[1] != '%') { + // A file name may contain spaces, but this isn't + // in "\f". For "%f:%l:%m" there may be a ":" in + // the file name. Use ".\{-1,}x" instead (x is + // the next character), the requirement that :999: + // follows should work. + STRCPY(ptr, ".\\{-1,}"); + ptr += 7; + } else { + // File name followed by '\\' or '%': include as + // many file name chars as possible. + STRCPY(ptr, "\\f\\+"); + ptr += 4; + } + } else { + char_u *srcptr = (char_u *)fmt_pat[idx].pattern; + while ((*ptr = *srcptr++) != NUL) { + ptr++; + } + } + *ptr++ = '\\'; + *ptr++ = ')'; + } else if (*efmp == '*') { + if (*++efmp == '[' || *efmp == '\\') { + if ((*ptr++ = *efmp) == '[') { // %*[^a-z0-9] etc. + if (efmp[1] == '^') { + *ptr++ = *++efmp; + } + if (efmp < efm + len) { + efmp++; + *ptr++ = *efmp; // could be ']' + while (efmp < efm + len) { + efmp++; + if ((*ptr++ = *efmp) == ']') { + break; + } + } + if (efmp == efm + len) { + EMSG(_("E374: Missing ] in format string")); + return -1; + } + } + } else if (efmp < efm + len) { // %*\D, %*\s etc. + efmp++; + *ptr++ = *efmp; + } + *ptr++ = '\\'; + *ptr++ = '+'; + } else { + // TODO(vim): scanf()-like: %*ud, %*3c, %*f, ... ? + snprintf((char *)errmsg, CMDBUFFSIZE + 1, + _("E375: Unsupported %%%c in format string"), *efmp); + EMSG(errmsg); + return -1; + } + } else if (vim_strchr((char_u *)"%\\.^$~[", *efmp) != NULL) { + *ptr++ = *efmp; // regexp magic characters + } else if (*efmp == '#') { + *ptr++ = '*'; + } else if (*efmp == '>') { + fmt_ptr->conthere = true; + } else if (efmp == efm + 1) { // analyse prefix + if (vim_strchr((char_u *)"+-", *efmp) != NULL) { + fmt_ptr->flags = *efmp++; + } + if (vim_strchr((char_u *)"DXAEWICZGOPQ", *efmp) != NULL) { + fmt_ptr->prefix = *efmp; + } else { + snprintf((char *)errmsg, CMDBUFFSIZE + 1, + _("E376: Invalid %%%c in format string prefix"), *efmp); + EMSG(errmsg); + return -1; + } + } else { + snprintf((char *)errmsg, CMDBUFFSIZE + 1, + _("E377: Invalid %%%c in format string"), *efmp); + EMSG(errmsg); + return -1; + } + } else { // copy normal character + if (*efmp == '\\' && efmp + 1 < efm + len) { + efmp++; + } else if (vim_strchr((char_u *)".*^$~[", *efmp) != NULL) { + *ptr++ = '\\'; // escape regexp atoms + } + if (*efmp) { + *ptr++ = *efmp; + } + } + } + *ptr++ = '$'; + *ptr = NUL; + + return 0; +} + +static void free_efm_list(efm_T **efm_first) +{ + for (efm_T *efm_ptr = *efm_first; efm_ptr != NULL; efm_ptr = *efm_first) { + *efm_first = efm_ptr->next; + vim_regfree(efm_ptr->prog); + xfree(efm_ptr); + } +} + +// Parse 'errorformat' option +static efm_T * parse_efm_option(char_u *efm) +{ + efm_T *fmt_ptr = NULL; + efm_T *fmt_first = NULL; + efm_T *fmt_last = NULL; + int len; + + size_t errmsglen = CMDBUFFSIZE + 1; + char_u *errmsg = xmalloc(errmsglen); + + // Get some space to modify the format string into. + size_t i = (FMT_PATTERNS * 3) + (STRLEN(efm) << 2); + for (int round = FMT_PATTERNS - 1; round >= 0; ) { + i += STRLEN(fmt_pat[round--].pattern); + } + i += 2; // "%f" can become two chars longer + char_u *fmtstr = xmalloc(i); + + while (efm[0] != NUL) { + // Allocate a new eformat structure and put it at the end of the list + fmt_ptr = (efm_T *)xcalloc(1, sizeof(efm_T)); + if (fmt_first == NULL) { // first one + fmt_first = fmt_ptr; + } else { + fmt_last->next = fmt_ptr; + } + fmt_last = fmt_ptr; + + // Isolate one part in the 'errorformat' option + for (len = 0; efm[len] != NUL && efm[len] != ','; len++) { + if (efm[len] == '\\' && efm[len + 1] != NUL) { + len++; + } + } + + if (efm_to_regpat(efm, len, fmt_ptr, fmtstr, errmsg) == -1) { + goto parse_efm_error; + } + if ((fmt_ptr->prog = vim_regcomp(fmtstr, RE_MAGIC + RE_STRING)) == NULL) { + goto parse_efm_error; + } + // Advance to next part + efm = skip_to_option_part(efm + len); // skip comma and spaces + } + + if (fmt_first == NULL) { // nothing found + EMSG(_("E378: 'errorformat' contains no pattern")); + } + + goto parse_efm_end; + +parse_efm_error: + free_efm_list(&fmt_first); + +parse_efm_end: + xfree(fmtstr); + xfree(errmsg); + + return fmt_first; +} + +// Read the errorfile "efile" into memory, line by line, building the error +// list. +// Alternative: when "efile" is NULL read errors from buffer "buf". +// Alternative: when "tv" is not NULL get errors from the string or list. +// Always use 'errorformat' from "buf" if there is a local value. +// Then "lnumfirst" and "lnumlast" specify the range of lines to use. +// Set the title of the list to "qf_title". +// Return -1 for error, number of errors for success. +static int +qf_init_ext( qf_info_T *qi, char_u *efile, buf_T *buf, @@ -192,8 +451,14 @@ qf_init_ext ( { char_u *namebuf; char_u *errmsg; + size_t errmsglen; char_u *pattern; - char_u *fmtstr = NULL; + char_u *growbuf = NULL; + size_t growbuflen; + size_t growbufsiz = 0; + char_u *linebuf = NULL; + size_t linelen = 0; + bool discard; int col = 0; bool use_viscol = false; char_u type = 0; @@ -201,49 +466,25 @@ qf_init_ext ( long lnum = 0L; int enr = 0; FILE *fd = NULL; - qfline_T *qfprev = NULL; /* init to make SASC shut up */ - char_u *efmp; - efm_T *fmt_first = NULL; - efm_T *fmt_last = NULL; + qfline_T *old_last = NULL; + static efm_T *fmt_first = NULL; efm_T *fmt_ptr; efm_T *fmt_start = NULL; char_u *efm; - char_u *ptr; - char_u *srcptr; - int len; + static char_u *last_efm = NULL; + size_t len; int i; - int round; int idx = 0; - bool multiline = false; - bool multiignore = false; - bool multiscan = false; int retval = -1; // default: return error flag - char_u *directory = NULL; - char_u *currfile = NULL; char_u *tail = NULL; + char_u *p_buf = NULL; char_u *p_str = NULL; listitem_T *p_li = NULL; - struct dir_stack_T *file_stack = NULL; regmatch_T regmatch; - static struct fmtpattern { - char_u convchar; - char *pattern; - } fmt_pat[FMT_PATTERNS] = - { - {'f', ".\\+"}, /* only used when at end */ - {'n', "\\d\\+"}, - {'l', "\\d\\+"}, - {'c', "\\d\\+"}, - {'t', "."}, - {'m', ".\\+"}, - {'r', ".*"}, - {'p', "[- .]*"}, - {'v', "\\d\\+"}, - {'s', ".\\+"} - }; namebuf = xmalloc(CMDBUFFSIZE + 1); - errmsg = xmalloc(CMDBUFFSIZE + 1); + errmsglen = CMDBUFFSIZE + 1; + errmsg = xmalloc(errmsglen); pattern = xmalloc(CMDBUFFSIZE + 1); if (efile != NULL && (fd = mch_fopen((char *)efile, "r")) == NULL) { @@ -251,14 +492,13 @@ qf_init_ext ( goto qf_init_end; } - if (newlist || qi->qf_curlist == qi->qf_listcount) - /* make place for a new list */ + if (newlist || qi->qf_curlist == qi->qf_listcount) { + // make place for a new list qf_new_list(qi, qf_title); - else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) - /* Adding to existing list, find last entry. */ - for (qfprev = qi->qf_lists[qi->qf_curlist].qf_start; - qfprev->qf_next != qfprev; qfprev = qfprev->qf_next) - ; + } else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) { + // Adding to existing list, use last entry. + old_last = qi->qf_lists[qi->qf_curlist].qf_last; + } /* * Each part of the format string is copied and modified from errorformat to @@ -269,172 +509,35 @@ qf_init_ext ( efm = buf->b_p_efm; else efm = errorformat; - /* - * Get some space to modify the format string into. - */ - size_t fmtstr_size = 3 * FMT_PATTERNS + 4 * STRLEN(efm); - for (round = FMT_PATTERNS; round > 0; ) { - fmtstr_size += STRLEN(fmt_pat[--round].pattern); - } -#ifdef COLON_IN_FILENAME - fmtstr_size += 12; // "%f" can become twelve chars longer -#else - fmtstr_size += 2; // "%f" can become two chars longer -#endif - fmtstr = xmalloc(fmtstr_size); - while (efm[0] != NUL) { - /* - * Allocate a new eformat structure and put it at the end of the list - */ - fmt_ptr = xcalloc(1, sizeof(efm_T)); - if (fmt_first == NULL) /* first one */ - fmt_first = fmt_ptr; - else - fmt_last->next = fmt_ptr; - fmt_last = fmt_ptr; - - /* - * Isolate one part in the 'errorformat' option - */ - for (len = 0; efm[len] != NUL && efm[len] != ','; ++len) - if (efm[len] == '\\' && efm[len + 1] != NUL) - ++len; + // 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; + } - /* - * Build regexp pattern from current 'errorformat' option - */ - ptr = fmtstr; - *ptr++ = '^'; - round = 0; - for (efmp = efm; efmp < efm + len; ++efmp) { - if (*efmp == '%') { - ++efmp; - for (idx = 0; idx < FMT_PATTERNS; ++idx) - if (fmt_pat[idx].convchar == *efmp) - break; - if (idx < FMT_PATTERNS) { - if (fmt_ptr->addr[idx]) { - sprintf((char *)errmsg, - _("E372: Too many %%%c in format string"), *efmp); - EMSG(errmsg); - goto error2; - } - if ((idx - && idx < 6 - && vim_strchr((char_u *)"DXOPQ", - fmt_ptr->prefix) != NULL) - || (idx == 6 - && vim_strchr((char_u *)"OPQ", - fmt_ptr->prefix) == NULL)) { - sprintf((char *)errmsg, - _("E373: Unexpected %%%c in format string"), *efmp); - EMSG(errmsg); - goto error2; - } - fmt_ptr->addr[idx] = (char_u)++ round; - *ptr++ = '\\'; - *ptr++ = '('; -#ifdef BACKSLASH_IN_FILENAME - if (*efmp == 'f') { - /* Also match "c:" in the file name, even when - * checking for a colon next: "%f:". - * "\%(\a:\)\=" */ - STRCPY(ptr, "\\%(\\a:\\)\\="); - ptr += 10; - } -#endif - if (*efmp == 'f' && efmp[1] != NUL) { - if (efmp[1] != '\\' && efmp[1] != '%') { - /* A file name may contain spaces, but this isn't - * in "\f". For "%f:%l:%m" there may be a ":" in - * the file name. Use ".\{-1,}x" instead (x is - * the next character), the requirement that :999: - * follows should work. */ - STRCPY(ptr, ".\\{-1,}"); - ptr += 7; - } else { - /* File name followed by '\\' or '%': include as - * many file name chars as possible. */ - STRCPY(ptr, "\\f\\+"); - ptr += 4; - } - } else { - srcptr = (char_u *)fmt_pat[idx].pattern; - while ((*ptr = *srcptr++) != NUL) - ++ptr; - } - *ptr++ = '\\'; - *ptr++ = ')'; - } else if (*efmp == '*') { - if (*++efmp == '[' || *efmp == '\\') { - if ((*ptr++ = *efmp) == '[') { /* %*[^a-z0-9] etc. */ - if (efmp[1] == '^') - *ptr++ = *++efmp; - if (efmp < efm + len) { - *ptr++ = *++efmp; /* could be ']' */ - while (efmp < efm + len - && (*ptr++ = *++efmp) != ']') - /* skip */; - if (efmp == efm + len) { - EMSG(_("E374: Missing ] in format string")); - goto error2; - } - } - } else if (efmp < efm + len) /* %*\D, %*\s etc. */ - *ptr++ = *++efmp; - *ptr++ = '\\'; - *ptr++ = '+'; - } else { - /* TODO: scanf()-like: %*ud, %*3c, %*f, ... ? */ - sprintf((char *)errmsg, - _("E375: Unsupported %%%c in format string"), *efmp); - EMSG(errmsg); - goto error2; - } - } else if (vim_strchr((char_u *)"%\\.^$~[", *efmp) != NULL) - *ptr++ = *efmp; /* regexp magic characters */ - else if (*efmp == '#') - *ptr++ = '*'; - else if (*efmp == '>') - fmt_ptr->conthere = TRUE; - else if (efmp == efm + 1) { /* analyse prefix */ - if (vim_strchr((char_u *)"+-", *efmp) != NULL) - fmt_ptr->flags = *efmp++; - if (vim_strchr((char_u *)"DXAEWICZGOPQ", *efmp) != NULL) - fmt_ptr->prefix = *efmp; - else { - sprintf((char *)errmsg, - _("E376: Invalid %%%c in format string prefix"), *efmp); - EMSG(errmsg); - goto error2; - } - } else { - sprintf((char *)errmsg, - _("E377: Invalid %%%c in format string"), *efmp); - EMSG(errmsg); - goto error2; - } - } else { /* copy normal character */ - if (*efmp == '\\' && efmp + 1 < efm + len) - ++efmp; - else if (vim_strchr((char_u *)".*^$~[", *efmp) != NULL) - *ptr++ = '\\'; /* escape regexp atoms */ - if (*efmp) - *ptr++ = *efmp; - } + // If the errorformat didn't change between calls, then reuse the previously + // parsed values. + if (last_efm == NULL || (STRCMP(last_efm, efm) != 0)) { + // free the previously parsed data + xfree(last_efm); + last_efm = NULL; + free_efm_list(&fmt_first); + + // parse the current 'efm' + fmt_first = parse_efm_option(efm); + if (fmt_first != NULL) { + last_efm = vim_strsave(efm); } - *ptr++ = '$'; - *ptr = NUL; - if ((fmt_ptr->prog = vim_regcomp(fmtstr, RE_MAGIC + RE_STRING)) == NULL) - goto error2; - /* - * Advance to next part - */ - efm = skip_to_option_part(efm + len); /* skip comma and spaces */ } - if (fmt_first == NULL) { /* nothing found */ - EMSG(_("E378: 'errorformat' contains no pattern")); + + if (fmt_first == NULL) { // nothing found goto error2; } @@ -466,36 +569,49 @@ qf_init_ext ( /* Get the next line from the supplied string */ char_u *p; - if (!*p_str) /* Reached the end of the string */ + if (*p_str == NUL) { // Reached the end of the string break; + } p = vim_strchr(p_str, '\n'); - if (p) - len = (int)(p - p_str + 1); - else - len = (int)STRLEN(p_str); + if (p != NULL) { + len = (size_t)(p - p_str) + 1; + } else { + len = STRLEN(p_str); + } - if (len > CMDBUFFSIZE - 2) - STRLCPY(IObuff, p_str, CMDBUFFSIZE - 1); - else - STRLCPY(IObuff, p_str, len + 1); + if (len > IOSIZE - 2) { + linebuf = qf_grow_linebuf(&growbuf, &growbufsiz, len, &linelen); + } else { + linebuf = IObuff; + linelen = len; + } + STRLCPY(linebuf, p_str, linelen + 1); + // Increment using len in order to discard the rest of the line if it + // exceeds LINE_MAXLEN. p_str += len; } else if (tv->v_type == VAR_LIST) { // Get the next line from the supplied list - while (p_li && (p_li->li_tv.v_type != VAR_STRING - || p_li->li_tv.vval.v_string == NULL)) { + while (p_li != NULL + && (p_li->li_tv.v_type != VAR_STRING + || p_li->li_tv.vval.v_string == NULL)) { p_li = p_li->li_next; // Skip non-string items } - if (!p_li) /* End of the list */ + if (p_li == NULL) { // End of the list break; + } - len = (int)STRLEN(p_li->li_tv.vval.v_string); - if (len > CMDBUFFSIZE - 2) - len = CMDBUFFSIZE - 2; + len = STRLEN(p_li->li_tv.vval.v_string); + if (len > IOSIZE - 2) { + linebuf = qf_grow_linebuf(&growbuf, &growbufsiz, len, &linelen); + } else { + linebuf = IObuff; + linelen = len; + } - STRLCPY(IObuff, p_li->li_tv.vval.v_string, len + 1); + STRLCPY(linebuf, p_li->li_tv.vval.v_string, linelen + 1); p_li = p_li->li_next; /* next item */ } @@ -503,21 +619,94 @@ qf_init_ext ( /* Get the next line from the supplied buffer */ if (buflnum > lnumlast) break; - STRLCPY(IObuff, ml_get_buf(buf, buflnum++, FALSE), - CMDBUFFSIZE - 1); + p_buf = ml_get_buf(buf, buflnum++, false); + len = STRLEN(p_buf); + if (len > IOSIZE - 2) { + linebuf = qf_grow_linebuf(&growbuf, &growbufsiz, len, &linelen); + } else { + linebuf = IObuff; + linelen = len; + } + STRLCPY(linebuf, p_buf, linelen + 1); } - } else if (fgets((char *)IObuff, CMDBUFFSIZE - 2, fd) == NULL) - break; + } else { + if (fgets((char *)IObuff, IOSIZE, fd) == NULL) { + break; + } + + discard = false; + linelen = STRLEN(IObuff); + if (linelen == IOSIZE - 1 && !(IObuff[linelen - 1] == '\n' +#ifdef USE_CRNL + || IObuff[linelen - 1] == '\r' +#endif + )) { // NOLINT(whitespace/parens) + // The current line exceeds IObuff, continue reading using growbuf + // until EOL or LINE_MAXLEN bytes is read. + if (growbuf == NULL) { + growbufsiz = 2 * (IOSIZE - 1); + growbuf = xmalloc(growbufsiz); + } + + // Copy the read part of the line, excluding null-terminator + memcpy(growbuf, IObuff, IOSIZE - 1); + growbuflen = linelen; + + for (;;) { + if (fgets((char *)growbuf + growbuflen, + (int)(growbufsiz - growbuflen), fd) == NULL) { + break; + } + linelen = STRLEN(growbuf + growbuflen); + growbuflen += linelen; + if (growbuf[growbuflen - 1] == '\n' +#ifdef USE_CRNL + || growbuf[growbuflen - 1] == '\r' +#endif + ) { + break; + } + if (growbufsiz == LINE_MAXLEN) { + discard = true; + break; + } - IObuff[CMDBUFFSIZE - 2] = NUL; /* for very long lines */ - remove_bom(IObuff); + growbufsiz = (2 * growbufsiz < LINE_MAXLEN) + ? 2 * growbufsiz : LINE_MAXLEN; + growbuf = xrealloc(growbuf, 2 * growbufsiz); + } - if ((efmp = vim_strrchr(IObuff, '\n')) != NULL) - *efmp = NUL; + while (discard) { + // The current line is longer than LINE_MAXLEN, continue reading but + // discard everything until EOL or EOF is reached. + if (fgets((char *)IObuff, IOSIZE, fd) == NULL + || STRLEN(IObuff) < IOSIZE - 1 + || IObuff[IOSIZE - 1] == '\n' #ifdef USE_CRNL - if ((efmp = vim_strrchr(IObuff, '\r')) != NULL) - *efmp = NUL; + || IObuff[IOSIZE - 1] == '\r' #endif + ) { + break; + } + } + + linebuf = growbuf; + linelen = growbuflen; + } else { + linebuf = IObuff; + } + } + + if (linelen > 0 && linebuf[linelen - 1] == '\n') { + linebuf[linelen - 1] = NUL; + } +#ifdef USE_CRNL + if (linelen > 0 && linebuf[linelen - 1] == '\r') { + linebuf[linelen - 1] = NUL; + } +#endif + + remove_bom(linebuf); /* If there was no %> item start at the first pattern */ if (fmt_start == NULL) @@ -533,12 +722,14 @@ qf_init_ext ( restofline: for (; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) { idx = fmt_ptr->prefix; - if (multiscan && vim_strchr((char_u *)"OPQ", idx) == NULL) + if (qi->qf_multiscan && vim_strchr((char_u *)"OPQ", idx) == NULL) { continue; + } namebuf[0] = NUL; pattern[0] = NUL; - if (!multiscan) + if (!qi->qf_multiscan) { errmsg[0] = NUL; + } lnum = 0; col = 0; use_viscol = false; @@ -547,10 +738,10 @@ restofline: tail = NULL; regmatch.regprog = fmt_ptr->prog; - int r = vim_regexec(®match, IObuff, (colnr_T)0); + int r = vim_regexec(®match, linebuf, (colnr_T)0); fmt_ptr->prog = regmatch.regprog; if (r) { - if ((idx == 'C' || idx == 'Z') && !multiline) { + if ((idx == 'C' || idx == 'Z') && !qi->qf_multiline) { continue; } if (vim_strchr((char_u *)"EWI", idx) != NULL) { @@ -596,12 +787,23 @@ restofline: continue; type = *regmatch.startp[i]; } - if (fmt_ptr->flags == '+' && !multiscan) /* %+ */ - STRCPY(errmsg, IObuff); - else if ((i = (int)fmt_ptr->addr[5]) > 0) { /* %m */ - if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) + if (fmt_ptr->flags == '+' && !qi->qf_multiscan) { // %+ + if (linelen > errmsglen) { + // linelen + null terminator + errmsg = xrealloc(errmsg, linelen + 1); + errmsglen = linelen + 1; + } + STRLCPY(errmsg, linebuf, linelen + 1); + } else if ((i = (int)fmt_ptr->addr[5]) > 0) { // %m + if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) { continue; - len = (int)(regmatch.endp[i] - regmatch.startp[i]); + } + len = (size_t)(regmatch.endp[i] - regmatch.startp[i]); + if (len > errmsglen) { + // len + null terminator + errmsg = xrealloc(errmsg, len + 1); + errmsglen = len + 1; + } STRLCPY(errmsg, regmatch.startp[i], len + 1); } if ((i = (int)fmt_ptr->addr[6]) > 0) { /* %r */ @@ -635,9 +837,10 @@ restofline: if ((i = (int)fmt_ptr->addr[9]) > 0) { /* %s */ if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) continue; - len = (int)(regmatch.endp[i] - regmatch.startp[i]); - if (len > CMDBUFFSIZE - 5) + len = (size_t)(regmatch.endp[i] - regmatch.startp[i]); + if (len > CMDBUFFSIZE - 5) { len = CMDBUFFSIZE - 5; + } STRCPY(pattern, "^\\V"); STRNCAT(pattern, regmatch.startp[i], len); pattern[len + 3] = '\\'; @@ -647,7 +850,7 @@ restofline: break; } } - multiscan = false; + qi->qf_multiscan = false; if (fmt_ptr == NULL || idx == 'D' || idx == 'X') { if (fmt_ptr != NULL) { @@ -656,17 +859,25 @@ restofline: EMSG(_("E379: Missing or empty directory name")); goto error2; } - if ((directory = qf_push_dir(namebuf, &dir_stack)) == NULL) + qi->qf_directory = qf_push_dir(namebuf, &qi->qf_dir_stack, false); + if (qi->qf_directory == NULL) { goto error2; - } else if (idx == 'X') /* leave directory */ - directory = qf_pop_dir(&dir_stack); + } + } else if (idx == 'X') { // leave directory + qi->qf_directory = qf_pop_dir(&qi->qf_dir_stack); + } } namebuf[0] = NUL; // no match found, remove file name lnum = 0; // don't jump to this line valid = false; - STRCPY(errmsg, IObuff); // copy whole line to error message + if (linelen > errmsglen) { + // linelen + null terminator + errmsg = xrealloc(errmsg, linelen + 1); + } + // copy whole line to error message + STRLCPY(errmsg, linebuf, linelen + 1); if (fmt_ptr == NULL) { - multiline = multiignore = false; + qi->qf_multiline = qi->qf_multiignore = false; } } else if (fmt_ptr != NULL) { /* honor %> item */ @@ -674,13 +885,15 @@ restofline: fmt_start = fmt_ptr; if (vim_strchr((char_u *)"AEWI", idx) != NULL) { - multiline = true; // start of a multi-line message - multiignore = false; // reset continuation + qi->qf_multiline = true; // start of a multi-line message + qi->qf_multiignore = false; // reset continuation } else if (vim_strchr((char_u *)"CZ", idx) - != NULL) { /* continuation of multi-line msg */ - if (qfprev == NULL) + != NULL) { // continuation of multi-line msg + qfline_T *qfprev = qi->qf_lists[qi->qf_curlist].qf_last; + if (qfprev == NULL) { goto error2; - if (*errmsg && !multiignore) { + } + if (*errmsg && !qi->qf_multiignore) { size_t len = STRLEN(qfprev->qf_text); qfprev->qf_text = xrealloc(qfprev->qf_text, len + STRLEN(errmsg) + 2); qfprev->qf_text[len] = '\n'; @@ -695,13 +908,15 @@ restofline: if (!qfprev->qf_col) qfprev->qf_col = col; qfprev->qf_viscol = use_viscol; - if (!qfprev->qf_fnum) - qfprev->qf_fnum = qf_get_fnum(directory, + if (!qfprev->qf_fnum) { + qfprev->qf_fnum = qf_get_fnum(qi, qi->qf_directory, *namebuf - || directory ? namebuf : currfile - && valid ? currfile : 0); + || qi->qf_directory + ? namebuf : qi->qf_currfile + && valid ? qi->qf_currfile : 0); + } if (idx == 'Z') { - multiline = multiignore = false; + qi->qf_multiline = qi->qf_multiignore = false; } line_breakcheck(); continue; @@ -710,41 +925,43 @@ restofline: valid = false; if (*namebuf == NUL || os_path_exists(namebuf)) { if (*namebuf && idx == 'P') { - currfile = qf_push_dir(namebuf, &file_stack); + qi->qf_currfile = qf_push_dir(namebuf, &qi->qf_file_stack, true); } else if (idx == 'Q') { - currfile = qf_pop_dir(&file_stack); + qi->qf_currfile = qf_pop_dir(&qi->qf_file_stack); } *namebuf = NUL; if (tail && *tail) { STRMOVE(IObuff, skipwhite(tail)); - multiscan = true; + qi->qf_multiscan = true; goto restofline; } } } if (fmt_ptr->flags == '-') { // generally exclude this line - if (multiline) { - multiignore = true; // also exclude continuation lines + if (qi->qf_multiline) { + // also exclude continuation lines + qi->qf_multiignore = true; } continue; } } - if (qf_add_entry(qi, &qfprev, - directory, - (*namebuf || directory) - ? namebuf - : ((currfile && valid) ? currfile : (char_u *)NULL), - 0, - errmsg, - lnum, - col, - use_viscol, - pattern, - enr, - type, - valid) == FAIL) + if (qf_add_entry(qi, + qi->qf_directory, + (*namebuf || qi->qf_directory) + ? namebuf : ((qi->qf_currfile && valid) + ? qi->qf_currfile : (char_u *)NULL), + 0, + errmsg, + lnum, + col, + use_viscol, + pattern, + enr, + type, + valid) == FAIL) { goto error2; + } line_breakcheck(); } if (fd == NULL || !ferror(fd)) { @@ -762,31 +979,25 @@ restofline: } /* return number of matches */ retval = qi->qf_lists[qi->qf_curlist].qf_count; - goto qf_init_ok; + goto qf_init_end; } EMSG(_(e_readerrf)); error2: qf_free(qi, qi->qf_curlist); qi->qf_listcount--; - if (qi->qf_curlist > 0) - --qi->qf_curlist; -qf_init_ok: - if (fd != NULL) - fclose(fd); - for (fmt_ptr = fmt_first; fmt_ptr != NULL; fmt_ptr = fmt_first) { - fmt_first = fmt_ptr->next; - vim_regfree(fmt_ptr->prog); - xfree(fmt_ptr); + if (qi->qf_curlist > 0) { + qi->qf_curlist--; } - qf_clean_dir_stack(&dir_stack); - qf_clean_dir_stack(&file_stack); qf_init_end: + if (fd != NULL) { + fclose(fd); + } xfree(namebuf); xfree(errmsg); xfree(pattern); - xfree(fmtstr); + xfree(growbuf); - qf_update_buffer(qi); + qf_update_buffer(qi, old_last); return retval; } @@ -869,7 +1080,6 @@ void qf_free_all(win_T *wp) /// Add an entry to the end of the list of errors. /// /// @param qi quickfix list -/// @param prevp nonnull pointer (to previously added entry or NULL) /// @param dir optional directory name /// @param fname file name or NULL /// @param bufnum buffer number or zero @@ -883,17 +1093,23 @@ void qf_free_all(win_T *wp) /// @param valid valid entry /// /// @returns OK or FAIL. -static int qf_add_entry(qf_info_T *qi, qfline_T **prevp, char_u *dir, - char_u *fname, int bufnum, char_u *mesg, long lnum, - int col, char_u vis_col, char_u *pattern, int nr, - char_u type, char_u valid) +static int qf_add_entry(qf_info_T *qi, char_u *dir, char_u *fname, int bufnum, + char_u *mesg, long lnum, int col, char_u vis_col, + char_u *pattern, int nr, char_u type, char_u valid) { qfline_T *qfp = xmalloc(sizeof(qfline_T)); + qfline_T **lastp; // pointer to qf_last or NULL + + if (bufnum != 0) { + buf_T *buf = buflist_findnr(bufnum); - if (bufnum != 0) qfp->qf_fnum = bufnum; - else - qfp->qf_fnum = qf_get_fnum(dir, fname); + if (buf != NULL) { + buf->b_has_qf_entry = true; + } + } else { + qfp->qf_fnum = qf_get_fnum(qi, dir, fname); + } qfp->qf_text = vim_strsave(mesg); qfp->qf_lnum = lnum; qfp->qf_col = col; @@ -909,21 +1125,22 @@ static int qf_add_entry(qf_info_T *qi, qfline_T **prevp, char_u *dir, qfp->qf_type = type; qfp->qf_valid = valid; + lastp = &qi->qf_lists[qi->qf_curlist].qf_last; if (qi->qf_lists[qi->qf_curlist].qf_count == 0) { /* first element in the list */ qi->qf_lists[qi->qf_curlist].qf_start = qfp; qi->qf_lists[qi->qf_curlist].qf_ptr = qfp; qi->qf_lists[qi->qf_curlist].qf_index = 0; - qfp->qf_prev = qfp; // first element points to itself + qfp->qf_prev = NULL; } else { - assert(*prevp); - qfp->qf_prev = *prevp; - (*prevp)->qf_next = qfp; - } - qfp->qf_next = qfp; /* last element points to itself */ - qfp->qf_cleared = FALSE; - *prevp = qfp; - ++qi->qf_lists[qi->qf_curlist].qf_count; + assert(*lastp); + qfp->qf_prev = *lastp; + (*lastp)->qf_next = qfp; + } + qfp->qf_next = NULL; + qfp->qf_cleared = false; + *lastp = qfp; + qi->qf_lists[qi->qf_curlist].qf_count++; if (qi->qf_lists[qi->qf_curlist].qf_index == 0 && qfp->qf_valid) { /* first valid entry */ qi->qf_lists[qi->qf_curlist].qf_index = @@ -1008,6 +1225,7 @@ void copy_loclist(win_T *from, win_T *to) to_qfl->qf_count = 0; to_qfl->qf_index = 0; to_qfl->qf_start = NULL; + to_qfl->qf_last = NULL; to_qfl->qf_ptr = NULL; if (from_qfl->qf_title != NULL) to_qfl->qf_title = vim_strsave(from_qfl->qf_title); @@ -1016,23 +1234,24 @@ void copy_loclist(win_T *from, win_T *to) if (from_qfl->qf_count) { qfline_T *from_qfp; - qfline_T *prevp = NULL; - - /* copy all the location entries in this list */ - for (i = 0, from_qfp = from_qfl->qf_start; i < from_qfl->qf_count; - ++i, from_qfp = from_qfp->qf_next) { - if (qf_add_entry(to->w_llist, &prevp, - NULL, - NULL, - 0, - from_qfp->qf_text, - from_qfp->qf_lnum, - from_qfp->qf_col, - from_qfp->qf_viscol, - from_qfp->qf_pattern, - from_qfp->qf_nr, - 0, - from_qfp->qf_valid) == FAIL) { + qfline_T *prevp; + + // copy all the location entries in this list + for (i = 0, from_qfp = from_qfl->qf_start; + i < from_qfl->qf_count && from_qfp != NULL; + i++, from_qfp = from_qfp->qf_next) { + if (qf_add_entry(to->w_llist, + NULL, + NULL, + 0, + from_qfp->qf_text, + from_qfp->qf_lnum, + from_qfp->qf_col, + from_qfp->qf_viscol, + from_qfp->qf_pattern, + from_qfp->qf_nr, + 0, + from_qfp->qf_valid) == FAIL) { qf_free_all(to); return; } @@ -1041,10 +1260,12 @@ void copy_loclist(win_T *from, win_T *to) * directory and file names are not supplied. So the qf_fnum * field is copied here. */ - prevp->qf_fnum = from_qfp->qf_fnum; /* file number */ - prevp->qf_type = from_qfp->qf_type; /* error type */ - if (from_qfl->qf_ptr == from_qfp) - to_qfl->qf_ptr = prevp; /* current location */ + prevp = to->w_llist->qf_lists[to->w_llist->qf_curlist].qf_last; + prevp->qf_fnum = from_qfp->qf_fnum; // file number + prevp->qf_type = from_qfp->qf_type; // error type + if (from_qfl->qf_ptr == from_qfp) { + to_qfl->qf_ptr = prevp; // current location + } } } @@ -1061,52 +1282,54 @@ void copy_loclist(win_T *from, win_T *to) to->w_llist->qf_curlist = qi->qf_curlist; /* current list */ } -/* - * get buffer number for file "dir.name" - */ -static int qf_get_fnum(char_u *directory, char_u *fname) +// Get buffer number for file "dir.name". +// Also sets the b_has_qf_entry flag. +static int qf_get_fnum(qf_info_T *qi, char_u *directory, char_u *fname) { - if (fname == NULL || *fname == NUL) /* no file name */ + char_u *ptr; + buf_T *buf; + if (fname == NULL || *fname == NUL) { // no file name return 0; - { - char_u *ptr; - int fnum; + } #ifdef BACKSLASH_IN_FILENAME - if (directory != NULL) - slash_adjust(directory); - slash_adjust(fname); + if (directory != NULL) { + slash_adjust(directory); + } + slash_adjust(fname); #endif - if (directory != NULL && !vim_isAbsName(fname)) { - ptr = (char_u *)concat_fnames((char *)directory, (char *)fname, TRUE); - /* - * Here we check if the file really exists. - * This should normally be true, but if make works without - * "leaving directory"-messages we might have missed a - * directory change. - */ - if (!os_path_exists(ptr)) { - xfree(ptr); - directory = qf_guess_filepath(fname); - if (directory) - ptr = (char_u *)concat_fnames((char *)directory, (char *)fname, TRUE); - else - ptr = vim_strsave(fname); - } - /* Use concatenated directory name and file name */ - fnum = buflist_add(ptr, 0); + if (directory != NULL && !vim_isAbsName(fname)) { + ptr = (char_u *)concat_fnames((char *)directory, (char *)fname, true); + // Here we check if the file really exists. + // This should normally be true, but if make works without + // "leaving directory"-messages we might have missed a + // directory change. + if (!os_path_exists(ptr)) { xfree(ptr); - return fnum; + directory = qf_guess_filepath(qi, fname); + if (directory) { + ptr = (char_u *)concat_fnames((char *)directory, (char *)fname, true); + } else { + ptr = vim_strsave(fname); + } } - return buflist_add(fname, 0); + // Use concatenated directory name and file name + buf = buflist_new(ptr, NULL, (linenr_T)0, 0); + xfree(ptr); + } else { + buf = buflist_new(fname, NULL, (linenr_T)0, 0); + } + if (buf == NULL) { + return 0; } + buf->b_has_qf_entry = true; + return buf->b_fnum; } -/* - * push dirbuf onto the directory stack and return pointer to actual dir or - * NULL on error - */ -static char_u *qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr) +// Push dirbuf onto the directory stack and return pointer to actual dir or +// NULL on error. +static char_u *qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr, + bool is_file_stack) { struct dir_stack_T *ds_ptr; @@ -1119,7 +1342,7 @@ static char_u *qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr) /* store directory on the stack */ if (vim_isAbsName(dirbuf) || (*stackptr)->next == NULL - || (*stackptr && dir_stack != *stackptr)) + || (*stackptr && is_file_stack)) (*stackptr)->dirname = vim_strsave(dirbuf); else { /* Okay we don't have an absolute path. @@ -1221,17 +1444,18 @@ 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(char_u *filename) +static char_u *qf_guess_filepath(qf_info_T *qi, char_u *filename) { struct dir_stack_T *ds_ptr; struct dir_stack_T *ds_tmp; char_u *fullname; - /* no dirs on the stack - there's nothing we can do */ - if (dir_stack == NULL) + // no dirs on the stack - there's nothing we can do + if (qi->qf_dir_stack == NULL) { return NULL; + } - ds_ptr = dir_stack->next; + ds_ptr = qi->qf_dir_stack->next; fullname = NULL; while (ds_ptr) { xfree(fullname); @@ -1246,16 +1470,15 @@ static char_u *qf_guess_filepath(char_u *filename) xfree(fullname); - /* clean up all dirs we already left */ - while (dir_stack->next != ds_ptr) { - ds_tmp = dir_stack->next; - dir_stack->next = dir_stack->next->next; + // 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; xfree(ds_tmp->dirname); xfree(ds_tmp); } - return ds_ptr==NULL ? NULL : ds_ptr->dirname; - + return ds_ptr == NULL ? NULL : ds_ptr->dirname; } /// When loading a file from the quickfix, the auto commands may modify it. @@ -1272,7 +1495,7 @@ static bool is_qf_entry_present(qf_info_T *qi, qfline_T *qf_ptr) // Search for the entry in the current list for (i = 0, qfp = qfl->qf_start; i < qfl->qf_count; i++, qfp = qfp->qf_next) { - if (qfp == qf_ptr) { + if (qfp == NULL || qfp == qf_ptr) { break; } } @@ -1853,7 +2076,10 @@ void qf_list(exarg_T *eap) } qfp = qfp->qf_next; - ++i; + if (qfp == NULL) { + break; + } + i++; os_breakcheck(); } } @@ -1925,9 +2151,9 @@ void qf_age(exarg_T *eap) static void qf_msg(qf_info_T *qi) { smsg(_("error list %d of %d; %d errors"), - qi->qf_curlist + 1, qi->qf_listcount, - qi->qf_lists[qi->qf_curlist].qf_count); - qf_update_buffer(qi); + qi->qf_curlist + 1, qi->qf_listcount, + qi->qf_lists[qi->qf_curlist].qf_count); + qf_update_buffer(qi, NULL); } /* @@ -1936,29 +2162,35 @@ static void qf_msg(qf_info_T *qi) static void qf_free(qf_info_T *qi, int idx) { qfline_T *qfp; - int stop = FALSE; + qfline_T *qfpnext; + bool stop = false; - while (qi->qf_lists[idx].qf_count) { - qfp = qi->qf_lists[idx].qf_start->qf_next; + while (qi->qf_lists[idx].qf_count && qi->qf_lists[idx].qf_start != NULL) { + qfp = qi->qf_lists[idx].qf_start; + qfpnext = qfp->qf_next; if (qi->qf_lists[idx].qf_title != NULL && !stop) { - xfree(qi->qf_lists[idx].qf_start->qf_text); - stop = (qi->qf_lists[idx].qf_start == qfp); - xfree(qi->qf_lists[idx].qf_start->qf_pattern); - xfree(qi->qf_lists[idx].qf_start); - if (stop) - /* Somehow qf_count may have an incorrect value, set it to 1 - * to avoid crashing when it's wrong. - * TODO: Avoid qf_count being incorrect. */ + xfree(qfp->qf_text); + stop = (qfp == qfpnext); + xfree(qfp->qf_pattern); + xfree(qfp); + if (stop) { + // 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; + } } - qi->qf_lists[idx].qf_start = qfp; - --qi->qf_lists[idx].qf_count; + qi->qf_lists[idx].qf_start = qfpnext; + qi->qf_lists[idx].qf_count--; } xfree(qi->qf_lists[idx].qf_title); qi->qf_lists[idx].qf_start = NULL; qi->qf_lists[idx].qf_ptr = NULL; qi->qf_lists[idx].qf_title = NULL; qi->qf_lists[idx].qf_index = 0; + + qf_clean_dir_stack(&qi->qf_dir_stack); + qf_clean_dir_stack(&qi->qf_file_stack); } /* @@ -1970,7 +2202,11 @@ void qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long qfline_T *qfp; int idx; qf_info_T *qi = &ql_info; + bool found_one = false; + if (!curbuf->b_has_qf_entry) { + return; + } if (wp != NULL) { if (wp->w_llist == NULL) return; @@ -1980,8 +2216,10 @@ void qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long for (idx = 0; idx < qi->qf_listcount; ++idx) if (qi->qf_lists[idx].qf_count) for (i = 0, qfp = qi->qf_lists[idx].qf_start; - i < qi->qf_lists[idx].qf_count; ++i, qfp = qfp->qf_next) + i < qi->qf_lists[idx].qf_count && qfp != NULL; + i++, qfp = qfp->qf_next) { if (qfp->qf_fnum == curbuf->b_fnum) { + found_one = true; if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2) { if (amount == MAXLNUM) qfp->qf_cleared = TRUE; @@ -1990,6 +2228,11 @@ void qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long } else if (amount_after && qfp->qf_lnum > line2) qfp->qf_lnum += amount_after; } + } + + if (!found_one) { + curbuf->b_has_qf_entry = false; + } } /* @@ -2195,7 +2438,7 @@ void ex_copen(exarg_T *eap) qf_set_title_var(qi); // Fill the buffer with the quickfix list. - qf_fill_buffer(qi); + qf_fill_buffer(qi, curbuf, NULL); curwin->w_cursor.lnum = qi->qf_lists[qi->qf_curlist].qf_index; curwin->w_cursor.col = 0; @@ -2203,6 +2446,44 @@ void ex_copen(exarg_T *eap) update_topline(); /* scroll to show the line */ } +// Move the cursor in the quickfix window to "lnum". +static void qf_win_goto(win_T *win, linenr_T lnum) +{ + win_T *old_curwin = curwin; + + curwin = win; + curbuf = win->w_buffer; + curwin->w_cursor.lnum = lnum; + curwin->w_cursor.col = 0; + curwin->w_cursor.coladd = 0; + curwin->w_curswant = 0; + update_topline(); // scroll to show the line + redraw_later(VALID); + curwin->w_redr_status = true; // update ruler + curwin = old_curwin; + curbuf = curwin->w_buffer; +} + +// :cbottom/:lbottom command. +void ex_cbottom(exarg_T *eap) +{ + qf_info_T *qi = &ql_info; + + if (eap->cmdidx == CMD_lbottom) { + qi = GET_LOC_LIST(curwin); + if (qi == NULL) { + EMSG(_(e_loclist)); + return; + } + } + + win_T *win = qf_find_win(qi); + + if (win != NULL && win->w_cursor.lnum != win->w_buffer->b_ml.ml_line_count) { + qf_win_goto(win, win->w_buffer->b_ml.ml_line_count); + } +} + /* * Return the number of the current entry (line number in the quickfix * window). @@ -2239,24 +2520,14 @@ qf_win_pos_update ( if (win != NULL && qf_index <= win->w_buffer->b_ml.ml_line_count && old_qf_index != qf_index) { - win_T *old_curwin = curwin; - - curwin = win; - curbuf = win->w_buffer; if (qf_index > old_qf_index) { - curwin->w_redraw_top = old_qf_index; - curwin->w_redraw_bot = qf_index; + win->w_redraw_top = old_qf_index; + win->w_redraw_bot = qf_index; } else { - curwin->w_redraw_top = qf_index; - curwin->w_redraw_bot = old_qf_index; + win->w_redraw_top = qf_index; + win->w_redraw_bot = old_qf_index; } - curwin->w_cursor.lnum = qf_index; - curwin->w_cursor.col = 0; - update_topline(); /* scroll to show the line */ - redraw_later(VALID); - curwin->w_redr_status = TRUE; /* update ruler */ - curwin = old_curwin; - curbuf = curwin->w_buffer; + qf_win_goto(win, qf_index); } return win != NULL; } @@ -2314,7 +2585,7 @@ static buf_T *qf_find_buf(qf_info_T *qi) /* * Find the quickfix buffer. If it exists, update the contents. */ -static void qf_update_buffer(qf_info_T *qi) +static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last) { buf_T *buf; win_T *win; @@ -2324,8 +2595,12 @@ static void qf_update_buffer(qf_info_T *qi) /* Check if a buffer for the quickfix list exists. Update it. */ buf = qf_find_buf(qi); if (buf != NULL) { - /* set curwin/curbuf to buf and save a few things */ - aucmd_prepbuf(&aco, buf); + linenr_T old_line_count = buf->b_ml.ml_line_count; + + if (old_last == NULL) { + // set curwin/curbuf to buf and save a few things + aucmd_prepbuf(&aco, buf); + } if ((win = qf_find_win(qi)) != NULL) { curwin_save = curwin; @@ -2333,12 +2608,21 @@ static void qf_update_buffer(qf_info_T *qi) qf_set_title_var(qi); curwin = curwin_save; } - qf_fill_buffer(qi); - /* restore curwin/curbuf and a few other things */ - aucmd_restbuf(&aco); + qf_fill_buffer(qi, buf, old_last); - (void)qf_win_pos_update(qi, 0); + if (old_last == NULL) { + (void)qf_win_pos_update(qi, 0); + + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); + } + + // Only redraw when added lines are visible. This avoids flickering when + // the added lines are not visible. + if ((win = qf_find_win(qi)) != NULL && old_line_count < win->w_botline) { + redraw_buf_later(buf, NOT_VALID); + } } } @@ -2351,11 +2635,12 @@ static void qf_set_title_var(qf_info_T *qi) } } -/* - * Fill current buffer with quickfix errors, replacing any previous contents. - * curbuf must be the quickfix buffer! - */ -static void qf_fill_buffer(qf_info_T *qi) +// Fill current buffer with quickfix errors, replacing any previous contents. +// curbuf must be the quickfix buffer! +// If "old_last" is not NULL append the items after this one. +// When "old_last" is NULL then "buf" must equal "curbuf"! Because ml_delete() +// is used and autocommands will be triggered. +static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last) { linenr_T lnum; qfline_T *qfp; @@ -2363,15 +2648,29 @@ static void qf_fill_buffer(qf_info_T *qi) int len; int old_KeyTyped = KeyTyped; - /* delete all existing lines */ - while ((curbuf->b_ml.ml_flags & ML_EMPTY) == 0) - (void)ml_delete((linenr_T)1, FALSE); + if (old_last == NULL) { + if (buf != curbuf) { + EMSG2(_(e_intern2), "qf_fill_buffer()"); + return; + } + + // delete all existing lines + while ((curbuf->b_ml.ml_flags & ML_EMPTY) == 0) { + (void)ml_delete((linenr_T)1, false); + } + } /* Check if there is anything to display */ if (qi->qf_curlist < qi->qf_listcount) { - /* Add one line for each error */ - qfp = qi->qf_lists[qi->qf_curlist].qf_start; - for (lnum = 0; lnum < qi->qf_lists[qi->qf_curlist].qf_count; ++lnum) { + // Add one line for each error + if (old_last == NULL) { + qfp = qi->qf_lists[qi->qf_curlist].qf_start; + lnum = 0; + } else { + qfp = old_last->qf_next; + lnum = buf->b_ml.ml_line_count; + } + while (lnum < qi->qf_lists[qi->qf_curlist].qf_count) { if (qfp->qf_fnum != 0 && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL && errbuf->b_fname != NULL) { @@ -2411,33 +2710,42 @@ static void qf_fill_buffer(qf_info_T *qi) qf_fmt_text(len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text, IObuff + len, IOSIZE - len); - if (ml_append(lnum, IObuff, (colnr_T)STRLEN(IObuff) + 1, FALSE) - == FAIL) + if (ml_append_buf(buf, lnum, IObuff, (colnr_T)STRLEN(IObuff) + 1, false) + == FAIL) { break; + } + lnum++; qfp = qfp->qf_next; + if (qfp == NULL) { + break; + } + } + if (old_last == NULL) { + // Delete the empty line which is now at the end + (void)ml_delete(lnum + 1, false); } - /* Delete the empty line which is now at the end */ - (void)ml_delete(lnum + 1, FALSE); } /* correct cursor position */ check_lnums(TRUE); - /* Set the 'filetype' to "qf" each time after filling the buffer. This - * resembles reading a file into a buffer, it's more logical when using - * autocommands. */ - set_option_value((char_u *)"ft", 0L, (char_u *)"qf", OPT_LOCAL); - curbuf->b_p_ma = FALSE; - - keep_filetype = TRUE; /* don't detect 'filetype' */ - apply_autocmds(EVENT_BUFREADPOST, (char_u *)"quickfix", NULL, - FALSE, curbuf); - apply_autocmds(EVENT_BUFWINENTER, (char_u *)"quickfix", NULL, - FALSE, curbuf); - keep_filetype = FALSE; - - /* make sure it will be redrawn */ - redraw_curbuf_later(NOT_VALID); + if (old_last == NULL) { + // Set the 'filetype' to "qf" each time after filling the buffer. This + // resembles reading a file into a buffer, it's more logical when using + // autocommands. + set_option_value((char_u *)"ft", 0L, (char_u *)"qf", OPT_LOCAL); + curbuf->b_p_ma = false; + + keep_filetype = true; // don't detect 'filetype' + apply_autocmds(EVENT_BUFREADPOST, (char_u *)"quickfix", NULL, + false, curbuf); + apply_autocmds(EVENT_BUFWINENTER, (char_u *)"quickfix", NULL, + false, curbuf); + keep_filetype = false; + + // make sure it will be redrawn + redraw_curbuf_later(NOT_VALID); + } /* Restore KeyTyped, setting 'filetype' may reset it. */ KeyTyped = old_KeyTyped; @@ -2963,7 +3271,6 @@ void ex_vimgrep(exarg_T *eap) int fi; qf_info_T *qi = &ql_info; qfline_T *cur_qf_start; - qfline_T *prevp = NULL; long lnum; buf_T *buf; int duplicate_name = FALSE; @@ -3048,12 +3355,6 @@ void ex_vimgrep(exarg_T *eap) || qi->qf_curlist == qi->qf_listcount) { // make place for a new list qf_new_list(qi, title != NULL ? title : *eap->cmdlinep); - } else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) { - // Adding to existing list, find last entry. - for (prevp = qi->qf_lists[qi->qf_curlist].qf_start; - prevp->qf_next != prevp; - prevp = prevp->qf_next) { - } } /* parse the list of arguments */ @@ -3149,23 +3450,22 @@ void ex_vimgrep(exarg_T *eap) ++lnum) { col = 0; while (vim_regexec_multi(®match, curwin, buf, lnum, - col, NULL) > 0) { - ; - if (qf_add_entry(qi, &prevp, - NULL, /* dir */ - fname, - 0, - ml_get_buf(buf, - regmatch.startpos[0].lnum + lnum, FALSE), - regmatch.startpos[0].lnum + lnum, - regmatch.startpos[0].col + 1, - FALSE, /* vis_col */ - NULL, /* search pattern */ - 0, /* nr */ - 0, /* type */ - TRUE /* valid */ - ) == FAIL) { - got_int = TRUE; + col, NULL) > 0) { + if (qf_add_entry(qi, + NULL, // dir + fname, + 0, + ml_get_buf(buf, + regmatch.startpos[0].lnum + lnum, false), + regmatch.startpos[0].lnum + lnum, + regmatch.startpos[0].col + 1, + false, // vis_col + NULL, // search pattern + 0, // nr + 0, // type + true) // valid + == FAIL) { + got_int = true; break; } found_match = TRUE; @@ -3240,7 +3540,7 @@ void ex_vimgrep(exarg_T *eap) 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; - qf_update_buffer(qi); + qf_update_buffer(qi, NULL); if (au_name != NULL) apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, @@ -3524,19 +3824,21 @@ int get_errorlist(win_T *wp, list_T *list) return FAIL; qfp = qfp->qf_next; + if (qfp == NULL) { + break; + } } return OK; } -/* - * Populate the quickfix list with the items supplied in the list - * of dictionaries. "title" will be copied to w:quickfix_title - */ +// Populate the quickfix list with the items supplied in the list +// of dictionaries. "title" will be copied to w:quickfix_title +// "action" is 'a' for add, 'r' for replace. Otherwise create a new list. int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) { listitem_T *li; dict_T *d; - qfline_T *prevp = NULL; + qfline_T *old_last = NULL; int retval = OK; qf_info_T *qi = &ql_info; bool did_bufnr_emsg = false; @@ -3545,15 +3847,13 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) qi = ll_get_or_alloc_list(wp); } - if (action == ' ' || qi->qf_curlist == qi->qf_listcount) - /* make place for a new list */ + if (action == ' ' || qi->qf_curlist == qi->qf_listcount) { + // make place for a new list qf_new_list(qi, title); - else if (action == 'a' && qi->qf_lists[qi->qf_curlist].qf_count > 0) - /* Adding to existing list, find last entry. */ - for (prevp = qi->qf_lists[qi->qf_curlist].qf_start; - prevp->qf_next != prevp; prevp = prevp->qf_next) - ; - else if (action == 'r') { + } else if (action == 'a' && qi->qf_lists[qi->qf_curlist].qf_count > 0) { + // Adding to existing list, use last entry. + old_last = qi->qf_lists[qi->qf_curlist].qf_last; + } else if (action == 'r') { qf_free(qi, qi->qf_curlist); qf_store_title(qi, title); } @@ -3595,7 +3895,6 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) } int status = qf_add_entry(qi, - &prevp, NULL, // dir filename, bufnum, @@ -3619,17 +3918,21 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) } } - if (qi->qf_lists[qi->qf_curlist].qf_index == 0) - /* no valid entry */ - qi->qf_lists[qi->qf_curlist].qf_nonevalid = TRUE; - else - qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE; - qi->qf_lists[qi->qf_curlist].qf_ptr = qi->qf_lists[qi->qf_curlist].qf_start; - if (qi->qf_lists[qi->qf_curlist].qf_count > 0) { - qi->qf_lists[qi->qf_curlist].qf_index = 1; + if (qi->qf_lists[qi->qf_curlist].qf_index == 0) { + // no valid entry + qi->qf_lists[qi->qf_curlist].qf_nonevalid = true; + } else { + qi->qf_lists[qi->qf_curlist].qf_nonevalid = false; + } + if (action != 'a') { + qi->qf_lists[qi->qf_curlist].qf_ptr = qi->qf_lists[qi->qf_curlist].qf_start; + if (qi->qf_lists[qi->qf_curlist].qf_count > 0) { + qi->qf_lists[qi->qf_curlist].qf_index = 1; + } } - qf_update_buffer(qi); + // Don't update the cursor in quickfix window when appending entries + qf_update_buffer(qi, old_last); return retval; } @@ -3734,7 +4037,6 @@ void ex_helpgrep(exarg_T *eap) char_u **fnames; FILE *fd; int fi; - qfline_T *prevp = NULL; long lnum; char_u *lang; qf_info_T *qi = &ql_info; @@ -3837,23 +4139,24 @@ void ex_helpgrep(exarg_T *eap) while (l > 0 && line[l - 1] <= ' ') line[--l] = NUL; - if (qf_add_entry(qi, &prevp, - NULL, /* dir */ - fnames[fi], - 0, - line, - lnum, - (int)(regmatch.startp[0] - line) - + 1, /* col */ - FALSE, /* vis_col */ - NULL, /* search pattern */ - 0, /* nr */ - 1, /* type */ - TRUE /* valid */ - ) == FAIL) { - got_int = TRUE; - if (line != IObuff) + if (qf_add_entry(qi, + NULL, // dir + fnames[fi], + 0, + line, + lnum, + (int)(regmatch.startp[0] - line) + + 1, // col + false, // vis_col + NULL, // search pattern + 0, // nr + 1, // type + true) // valid + == FAIL) { + got_int = true; + if (line != IObuff) { xfree(line); + } break; } } @@ -3885,7 +4188,7 @@ void ex_helpgrep(exarg_T *eap) /* Darn, some plugin changed the value. */ free_string_option(save_cpo); - qf_update_buffer(qi); + qf_update_buffer(qi, NULL); if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 0118597eb8..7cd1921ce1 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -30,15 +30,16 @@ SCRIPTS := \ # Tests using runtest.vim.vim. # Keep test_alot*.res as the last one, sort the others. NEW_TESTS = \ + test_cmdline.res \ test_cscope.res \ - test_cmdline.res \ test_diffmode.res \ test_hardcopy.res \ test_help_tagjump.res \ test_history.res \ test_langmap.res \ - test_match.res \ + test_match.res \ test_matchadd_conceal.res \ + test_quickfix.res \ test_syntax.res \ test_usercommands.res \ test_timers.res \ diff --git a/src/nvim/testdir/samples/quickfix.txt b/src/nvim/testdir/samples/quickfix.txt new file mode 100644 index 0000000000..2de3835473 --- /dev/null +++ b/src/nvim/testdir/samples/quickfix.txt @@ -0,0 +1,4 @@ +samples/quickfix.txt:1:1:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +samples/quickfix.txt:2:1:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +samples/quickfix.txt:3:1:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +samples/quickfix.txt:4:1:dddddddddd diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim new file mode 100644 index 0000000000..f30902b915 --- /dev/null +++ b/src/nvim/testdir/test_quickfix.vim @@ -0,0 +1,1422 @@ +" Test for the quickfix commands. + +if !has('quickfix') + finish +endif + +set encoding=utf-8 + +function! s:setup_commands(cchar) + if a:cchar == 'c' + command! -nargs=* -bang Xlist <mods>clist<bang> <args> + command! -nargs=* Xgetexpr <mods>cgetexpr <args> + command! -nargs=* Xaddexpr <mods>caddexpr <args> + command! -nargs=* Xolder <mods>colder <args> + command! -nargs=* Xnewer <mods>cnewer <args> + command! -nargs=* Xopen <mods>copen <args> + command! -nargs=* Xwindow <mods>cwindow <args> + command! -nargs=* Xbottom <mods>cbottom <args> + command! -nargs=* Xclose <mods>cclose <args> + command! -nargs=* -bang Xfile <mods>cfile<bang> <args> + command! -nargs=* Xgetfile <mods>cgetfile <args> + command! -nargs=* Xaddfile <mods>caddfile <args> + command! -nargs=* -bang Xbuffer <mods>cbuffer<bang> <args> + command! -nargs=* Xgetbuffer <mods>cgetbuffer <args> + command! -nargs=* Xaddbuffer <mods>caddbuffer <args> + command! -nargs=* Xrewind <mods>crewind <args> + command! -nargs=* -bang Xnext <mods>cnext<bang> <args> + command! -nargs=* -bang Xprev <mods>cprev<bang> <args> + command! -nargs=* -bang Xfirst <mods>cfirst<bang> <args> + command! -nargs=* -bang Xlast <mods>clast<bang> <args> + command! -nargs=* -bang Xnfile <mods>cnfile<bang> <args> + command! -nargs=* -bang Xpfile <mods>cpfile<bang> <args> + command! -nargs=* Xexpr <mods>cexpr <args> + command! -nargs=* Xvimgrep <mods>vimgrep <args> + command! -nargs=* Xgrep <mods> grep <args> + command! -nargs=* Xgrepadd <mods> grepadd <args> + command! -nargs=* Xhelpgrep helpgrep <args> + let g:Xgetlist = function('getqflist') + let g:Xsetlist = function('setqflist') + else + command! -nargs=* -bang Xlist <mods>llist<bang> <args> + command! -nargs=* Xgetexpr <mods>lgetexpr <args> + command! -nargs=* Xaddexpr <mods>laddexpr <args> + command! -nargs=* Xolder <mods>lolder <args> + command! -nargs=* Xnewer <mods>lnewer <args> + command! -nargs=* Xopen <mods>lopen <args> + command! -nargs=* Xwindow <mods>lwindow <args> + command! -nargs=* Xbottom <mods>lbottom <args> + command! -nargs=* Xclose <mods>lclose <args> + command! -nargs=* -bang Xfile <mods>lfile<bang> <args> + command! -nargs=* Xgetfile <mods>lgetfile <args> + command! -nargs=* Xaddfile <mods>laddfile <args> + command! -nargs=* -bang Xbuffer <mods>lbuffer<bang> <args> + command! -nargs=* Xgetbuffer <mods>lgetbuffer <args> + command! -nargs=* Xaddbuffer <mods>laddbuffer <args> + command! -nargs=* Xrewind <mods>lrewind <args> + command! -nargs=* -bang Xnext <mods>lnext<bang> <args> + command! -nargs=* -bang Xprev <mods>lprev<bang> <args> + command! -nargs=* -bang Xfirst <mods>lfirst<bang> <args> + command! -nargs=* -bang Xlast <mods>llast<bang> <args> + command! -nargs=* -bang Xnfile <mods>lnfile<bang> <args> + command! -nargs=* -bang Xpfile <mods>lpfile<bang> <args> + command! -nargs=* Xexpr <mods>lexpr <args> + command! -nargs=* Xvimgrep <mods>lvimgrep <args> + command! -nargs=* Xgrep <mods> lgrep <args> + command! -nargs=* Xgrepadd <mods> lgrepadd <args> + command! -nargs=* Xhelpgrep lhelpgrep <args> + let g:Xgetlist = function('getloclist', [0]) + let g:Xsetlist = function('setloclist', [0]) + endif +endfunction + +" Tests for the :clist and :llist commands +function XlistTests(cchar) + call s:setup_commands(a:cchar) + + " With an empty list, command should return error + Xgetexpr [] + silent! Xlist + call assert_true(v:errmsg ==# 'E42: No Errors') + + " Populate the list and then try + Xgetexpr ['non-error 1', 'Xtestfile1:1:3:Line1', + \ 'non-error 2', 'Xtestfile2:2:2:Line2', + \ 'non-error 3', 'Xtestfile3:3:1:Line3'] + + " List only valid entries + redir => result + Xlist + redir END + let l = split(result, "\n") + call assert_equal([' 2 Xtestfile1:1 col 3: Line1', + \ ' 4 Xtestfile2:2 col 2: Line2', + \ ' 6 Xtestfile3:3 col 1: Line3'], l) + + " List all the entries + redir => result + Xlist! + redir END + let l = split(result, "\n") + call assert_equal([' 1: non-error 1', ' 2 Xtestfile1:1 col 3: Line1', + \ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2', + \ ' 5: non-error 3', ' 6 Xtestfile3:3 col 1: Line3'], l) + + " List a range of errors + redir => result + Xlist 3,6 + redir END + let l = split(result, "\n") + call assert_equal([' 4 Xtestfile2:2 col 2: Line2', + \ ' 6 Xtestfile3:3 col 1: Line3'], l) + + redir => result + Xlist! 3,4 + redir END + let l = split(result, "\n") + call assert_equal([' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l) + + redir => result + Xlist -6,-4 + redir END + let l = split(result, "\n") + call assert_equal([' 2 Xtestfile1:1 col 3: Line1'], l) + + redir => result + Xlist! -5,-3 + redir END + let l = split(result, "\n") + call assert_equal([' 2 Xtestfile1:1 col 3: Line1', + \ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l) +endfunction + +function Test_clist() + call XlistTests('c') + call XlistTests('l') +endfunction + +" Tests for the :colder, :cnewer, :lolder and :lnewer commands +" Note that this test assumes that a quickfix/location list is +" already set by the caller. +function XageTests(cchar) + call s:setup_commands(a:cchar) + + " Jumping to a non existent list should return error + silent! Xolder 99 + call assert_true(v:errmsg ==# 'E380: At bottom of quickfix stack') + + silent! Xnewer 99 + call assert_true(v:errmsg ==# 'E381: At top of quickfix stack') + + " Add three quickfix/location lists + Xgetexpr ['Xtestfile1:1:3:Line1'] + Xgetexpr ['Xtestfile2:2:2:Line2'] + Xgetexpr ['Xtestfile3:3:1:Line3'] + + " Go back two lists + Xolder + let l = g:Xgetlist() + call assert_equal('Line2', l[0].text) + + " Go forward two lists + Xnewer + let l = g:Xgetlist() + call assert_equal('Line3', l[0].text) + + " Test for the optional count argument + Xolder 2 + let l = g:Xgetlist() + call assert_equal('Line1', l[0].text) + + Xnewer 2 + let l = g:Xgetlist() + call assert_equal('Line3', l[0].text) +endfunction + +function Test_cage() + let list = [{'bufnr': 1, 'lnum': 1}] + call setqflist(list) + call XageTests('c') + + call setloclist(0, list) + call XageTests('l') +endfunction + +" Tests for the :cwindow, :lwindow :cclose, :lclose, :copen and :lopen +" commands +function XwindowTests(cchar) + call s:setup_commands(a:cchar) + + " Create a list with no valid entries + Xgetexpr ['non-error 1', 'non-error 2', 'non-error 3'] + + " Quickfix/Location window should not open with no valid errors + Xwindow + call assert_true(winnr('$') == 1) + + " Create a list with valid entries + Xgetexpr ['Xtestfile1:1:3:Line1', 'Xtestfile2:2:2:Line2', + \ 'Xtestfile3:3:1:Line3'] + + " Open the window + Xwindow + call assert_true(winnr('$') == 2 && winnr() == 2 && + \ getline('.') ==# 'Xtestfile1|1 col 3| Line1') + redraw! + + " Close the window + Xclose + call assert_true(winnr('$') == 1) + + " Create a list with no valid entries + Xgetexpr ['non-error 1', 'non-error 2', 'non-error 3'] + + " Open the window + Xopen 5 + call assert_true(winnr('$') == 2 && getline('.') ==# '|| non-error 1' + \ && winheight('.') == 5) + + " Opening the window again, should move the cursor to that window + wincmd t + Xopen 7 + call assert_true(winnr('$') == 2 && winnr() == 2 && + \ winheight('.') == 7 && + \ getline('.') ==# '|| non-error 1') + + + " Calling cwindow should close the quickfix window with no valid errors + Xwindow + call assert_true(winnr('$') == 1) +endfunction + +function Test_cwindow() + call XwindowTests('c') + call XwindowTests('l') +endfunction + +" Tests for the :cfile, :lfile, :caddfile, :laddfile, :cgetfile and :lgetfile +" commands. +function XfileTests(cchar) + call s:setup_commands(a:cchar) + + call writefile(['Xtestfile1:700:10:Line 700', + \ 'Xtestfile2:800:15:Line 800'], 'Xqftestfile1') + + enew! + Xfile Xqftestfile1 + let l = g:Xgetlist() + call assert_true(len(l) == 2 && + \ l[0].lnum == 700 && l[0].col == 10 && l[0].text ==# 'Line 700' && + \ l[1].lnum == 800 && l[1].col == 15 && l[1].text ==# 'Line 800') + + " Test with a non existent file + call assert_fails('Xfile non_existent_file', 'E40') + + " Run cfile/lfile from a modified buffer + enew! + silent! put ='Quickfix' + silent! Xfile Xqftestfile1 + call assert_true(v:errmsg ==# 'E37: No write since last change (add ! to override)') + + call writefile(['Xtestfile3:900:30:Line 900'], 'Xqftestfile1') + Xaddfile Xqftestfile1 + let l = g:Xgetlist() + call assert_true(len(l) == 3 && + \ l[2].lnum == 900 && l[2].col == 30 && l[2].text ==# 'Line 900') + + call writefile(['Xtestfile1:222:77:Line 222', + \ 'Xtestfile2:333:88:Line 333'], 'Xqftestfile1') + + enew! + Xgetfile Xqftestfile1 + let l = g:Xgetlist() + call assert_true(len(l) == 2 && + \ l[0].lnum == 222 && l[0].col == 77 && l[0].text ==# 'Line 222' && + \ l[1].lnum == 333 && l[1].col == 88 && l[1].text ==# 'Line 333') + + call delete('Xqftestfile1') +endfunction + +function Test_cfile() + call XfileTests('c') + call XfileTests('l') +endfunction + +" Tests for the :cbuffer, :lbuffer, :caddbuffer, :laddbuffer, :cgetbuffer and +" :lgetbuffer commands. +function XbufferTests(cchar) + call s:setup_commands(a:cchar) + + enew! + silent! call setline(1, ['Xtestfile7:700:10:Line 700', + \ 'Xtestfile8:800:15:Line 800']) + Xbuffer! + let l = g:Xgetlist() + call assert_true(len(l) == 2 && + \ l[0].lnum == 700 && l[0].col == 10 && l[0].text ==# 'Line 700' && + \ l[1].lnum == 800 && l[1].col == 15 && l[1].text ==# 'Line 800') + + enew! + silent! call setline(1, ['Xtestfile9:900:55:Line 900', + \ 'Xtestfile10:950:66:Line 950']) + Xgetbuffer + let l = g:Xgetlist() + call assert_true(len(l) == 2 && + \ l[0].lnum == 900 && l[0].col == 55 && l[0].text ==# 'Line 900' && + \ l[1].lnum == 950 && l[1].col == 66 && l[1].text ==# 'Line 950') + + enew! + silent! call setline(1, ['Xtestfile11:700:20:Line 700', + \ 'Xtestfile12:750:25:Line 750']) + Xaddbuffer + let l = g:Xgetlist() + call assert_true(len(l) == 4 && + \ l[1].lnum == 950 && l[1].col == 66 && l[1].text ==# 'Line 950' && + \ l[2].lnum == 700 && l[2].col == 20 && l[2].text ==# 'Line 700' && + \ l[3].lnum == 750 && l[3].col == 25 && l[3].text ==# 'Line 750') + enew! + +endfunction + +function Test_cbuffer() + call XbufferTests('c') + call XbufferTests('l') +endfunction + +function XexprTests(cchar) + call s:setup_commands(a:cchar) + + call assert_fails('Xexpr 10', 'E777:') +endfunction + +function Test_cexpr() + call XexprTests('c') + call XexprTests('l') +endfunction + +" Tests for :cnext, :cprev, :cfirst, :clast commands +function Xtest_browse(cchar) + call s:setup_commands(a:cchar) + + call s:create_test_file('Xqftestfile1') + call s:create_test_file('Xqftestfile2') + + Xgetexpr ['Xqftestfile1:5:Line5', + \ 'Xqftestfile1:6:Line6', + \ 'Xqftestfile2:10:Line10', + \ 'Xqftestfile2:11:Line11'] + + Xfirst + call assert_fails('Xprev', 'E553') + call assert_fails('Xpfile', 'E553') + Xnfile + call assert_equal('Xqftestfile2', bufname('%')) + call assert_equal(10, line('.')) + Xpfile + call assert_equal('Xqftestfile1', bufname('%')) + call assert_equal(6, line('.')) + Xlast + call assert_equal('Xqftestfile2', bufname('%')) + call assert_equal(11, line('.')) + call assert_fails('Xnext', 'E553') + call assert_fails('Xnfile', 'E553') + Xrewind + call assert_equal('Xqftestfile1', bufname('%')) + call assert_equal(5, line('.')) + + call delete('Xqftestfile1') + call delete('Xqftestfile2') +endfunction + +function Test_browse() + call Xtest_browse('c') + call Xtest_browse('l') +endfunction + +function! s:test_xhelpgrep(cchar) + call s:setup_commands(a:cchar) + Xhelpgrep quickfix + Xopen + if a:cchar == 'c' + let title_text = ':helpgrep quickfix' + else + let title_text = ':lhelpgrep quickfix' + endif + call assert_true(w:quickfix_title =~ title_text, w:quickfix_title) + " This wipes out the buffer, make sure that doesn't cause trouble. + Xclose +endfunction + +function Test_helpgrep() + call s:test_xhelpgrep('c') + call s:test_xhelpgrep('l') +endfunc + +func Test_errortitle() + augroup QfBufWinEnter + au! + au BufWinEnter * :let g:a=get(w:, 'quickfix_title', 'NONE') + augroup END + copen + let a=[{'lnum': 308, 'bufnr': bufnr(''), 'col': 58, 'valid': 1, 'vcol': 0, 'nr': 0, 'type': '', 'pattern': '', 'text': ' au BufWinEnter * :let g:a=get(w:, ''quickfix_title'', ''NONE'')'}] + call setqflist(a) + call assert_equal(':setqflist()', g:a) + augroup QfBufWinEnter + au! + augroup END + augroup! QfBufWinEnter +endfunc + +func Test_vimgreptitle() + augroup QfBufWinEnter + au! + au BufWinEnter * :let g:a=get(w:, 'quickfix_title', 'NONE') + augroup END + try + vimgrep /pattern/j file + catch /E480/ + endtry + copen + call assert_equal(': vimgrep /pattern/j file', g:a) + augroup QfBufWinEnter + au! + augroup END + augroup! QfBufWinEnter +endfunc + +function XqfTitleTests(cchar) + call s:setup_commands(a:cchar) + + Xgetexpr ['file:1:1:message'] + let l = g:Xgetlist() + if a:cchar == 'c' + call setqflist(l, 'r') + else + call setloclist(0, l, 'r') + endif + + Xopen + if a:cchar == 'c' + let title = ':setqflist()' + else + let title = ':setloclist()' + endif + call assert_equal(title, w:quickfix_title) + Xclose +endfunction + +" Tests for quickfix window's title +function Test_qf_title() + call XqfTitleTests('c') + call XqfTitleTests('l') +endfunction + +" Tests for 'errorformat' +function Test_efm() + let save_efm = &efm + set efm=%EEEE%m,%WWWW%m,%+CCCC%.%#,%-GGGG%.%# + cgetexpr ['WWWW', 'EEEE', 'CCCC'] + let l = strtrans(string(map(getqflist(), '[v:val.text, v:val.valid]'))) + call assert_equal("[['W', 1], ['E^@CCCC', 1]]", l) + cgetexpr ['WWWW', 'GGGG', 'EEEE', 'CCCC'] + let l = strtrans(string(map(getqflist(), '[v:val.text, v:val.valid]'))) + call assert_equal("[['W', 1], ['E^@CCCC', 1]]", l) + cgetexpr ['WWWW', 'GGGG', 'ZZZZ', 'EEEE', 'CCCC', 'YYYY'] + let l = strtrans(string(map(getqflist(), '[v:val.text, v:val.valid]'))) + call assert_equal("[['W', 1], ['ZZZZ', 0], ['E^@CCCC', 1], ['YYYY', 0]]", l) + let &efm = save_efm +endfunction + +" This will test for problems in quickfix: +" A. incorrectly copying location lists which caused the location list to show +" a different name than the file that was actually being displayed. +" B. not reusing the window for which the location list window is opened but +" instead creating new windows. +" C. make sure that the location list window is not reused instead of the +" window it belongs to. +" +" Set up the test environment: +function! ReadTestProtocol(name) + let base = substitute(a:name, '\v^test://(.*)%(\.[^.]+)?', '\1', '') + let word = substitute(base, '\v(.*)\..*', '\1', '') + + setl modifiable + setl noreadonly + setl noswapfile + setl bufhidden=delete + %del _ + " For problem 2: + " 'buftype' has to be set to reproduce the constant opening of new windows + setl buftype=nofile + + call setline(1, word) + + setl nomodified + setl nomodifiable + setl readonly + exe 'doautocmd BufRead ' . substitute(a:name, '\v^test://(.*)', '\1', '') +endfunction + +function Test_locationlist() + enew + + augroup testgroup + au! + autocmd BufReadCmd test://* call ReadTestProtocol(expand("<amatch>")) + augroup END + + let words = [ "foo", "bar", "baz", "quux", "shmoo", "spam", "eggs" ] + + let qflist = [] + for word in words + call add(qflist, {'filename': 'test://' . word . '.txt', 'text': 'file ' . word . '.txt', }) + " NOTE: problem 1: + " intentionally not setting 'lnum' so that the quickfix entries are not + " valid + call setloclist(0, qflist, ' ') + endfor + + " Test A + lrewind + enew + lopen + lnext + lnext + lnext + lnext + vert split + wincmd L + lopen + wincmd p + lnext + let fileName = expand("%") + wincmd p + let locationListFileName = substitute(getline(line('.')), '\([^|]*\)|.*', '\1', '') + let fileName = substitute(fileName, '\\', '/', 'g') + let locationListFileName = substitute(locationListFileName, '\\', '/', 'g') + call assert_equal("test://bar.txt", fileName) + call assert_equal("test://bar.txt", locationListFileName) + + wincmd n | only + + " Test B: + lrewind + lopen + 2 + exe "normal \<CR>" + wincmd p + 3 + exe "normal \<CR>" + wincmd p + 4 + exe "normal \<CR>" + call assert_equal(2, winnr('$')) + wincmd n | only + + " Test C: + lrewind + lopen + " Let's move the location list window to the top to check whether it (the + " first window found) will be reused when we try to open new windows: + wincmd K + 2 + exe "normal \<CR>" + wincmd p + 3 + exe "normal \<CR>" + wincmd p + 4 + exe "normal \<CR>" + 1wincmd w + call assert_equal('quickfix', &buftype) + 2wincmd w + let bufferName = expand("%") + let bufferName = substitute(bufferName, '\\', '/', 'g') + call assert_equal('test://quux.txt', bufferName) + + wincmd n | only + + augroup! testgroup +endfunction + +function Test_locationlist_curwin_was_closed() + augroup testgroup + au! + autocmd BufReadCmd test_curwin.txt call R(expand("<amatch>")) + augroup END + + function! R(n) + quit + endfunc + + new + let q = [] + call add(q, {'filename': 'test_curwin.txt' }) + call setloclist(0, q) + call assert_fails('lrewind', 'E924:') + + augroup! testgroup +endfunction + +" More tests for 'errorformat' +function! Test_efm1() + if !has('unix') + " The 'errorformat' setting is different on non-Unix systems. + " This test works only on Unix-like systems. + return + endif + + let l = [ + \ '"Xtestfile", line 4.12: 1506-045 (S) Undeclared identifier fd_set.', + \ '"Xtestfile", line 6 col 19; this is an error', + \ 'gcc -c -DHAVE_CONFIsing-prototypes -I/usr/X11R6/include version.c', + \ 'Xtestfile:9: parse error before `asd''', + \ 'make: *** [vim] Error 1', + \ 'in file "Xtestfile" linenr 10: there is an error', + \ '', + \ '2 returned', + \ '"Xtestfile", line 11 col 1; this is an error', + \ '"Xtestfile", line 12 col 2; this is another error', + \ '"Xtestfile", line 14:10; this is an error in column 10', + \ '=Xtestfile=, line 15:10; this is another error, but in vcol 10 this time', + \ '"Xtestfile", linenr 16: yet another problem', + \ 'Error in "Xtestfile" at line 17:', + \ 'x should be a dot', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 17', + \ ' ^', + \ 'Error in "Xtestfile" at line 18:', + \ 'x should be a dot', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 18', + \ '.............^', + \ 'Error in "Xtestfile" at line 19:', + \ 'x should be a dot', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 19', + \ '--------------^', + \ 'Error in "Xtestfile" at line 20:', + \ 'x should be a dot', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 20', + \ ' ^', + \ '', + \ 'Does anyone know what is the problem and how to correction it?', + \ '"Xtestfile", line 21 col 9: What is the title of the quickfix window?', + \ '"Xtestfile", line 22 col 9: What is the title of the quickfix window?' + \ ] + + call writefile(l, 'Xerrorfile1') + call writefile(l[:-2], 'Xerrorfile2') + + let m = [ + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 2', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 3', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 4', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 5', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 6', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 7', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 8', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 9', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 10', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 11', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 12', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 13', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 14', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 15', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 16', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 17', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 18', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 19', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 20', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 21', + \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 22' + \ ] + call writefile(m, 'Xtestfile') + + let save_efm = &efm + set efm+==%f=\\,\ line\ %l%*\\D%v%*[^\ ]\ %m + set efm^=%AError\ in\ \"%f\"\ at\ line\ %l:,%Z%p^,%C%m + + exe 'cf Xerrorfile2' + clast + copen + call assert_equal(':cf Xerrorfile2', w:quickfix_title) + wincmd p + + exe 'cf Xerrorfile1' + call assert_equal([4, 12], [line('.'), col('.')]) + cn + call assert_equal([6, 19], [line('.'), col('.')]) + cn + call assert_equal([9, 2], [line('.'), col('.')]) + cn + call assert_equal([10, 2], [line('.'), col('.')]) + cn + call assert_equal([11, 1], [line('.'), col('.')]) + cn + call assert_equal([12, 2], [line('.'), col('.')]) + cn + call assert_equal([14, 10], [line('.'), col('.')]) + cn + call assert_equal([15, 3, 10], [line('.'), col('.'), virtcol('.')]) + cn + call assert_equal([16, 2], [line('.'), col('.')]) + cn + call assert_equal([17, 6], [line('.'), col('.')]) + cn + call assert_equal([18, 7], [line('.'), col('.')]) + cn + call assert_equal([19, 8], [line('.'), col('.')]) + cn + call assert_equal([20, 9], [line('.'), col('.')]) + clast + cprev + cprev + wincmd w + call assert_equal(':cf Xerrorfile1', w:quickfix_title) + wincmd p + + let &efm = save_efm + call delete('Xerrorfile1') + call delete('Xerrorfile2') + call delete('Xtestfile') +endfunction + +" Test for quickfix directory stack support +function! s:dir_stack_tests(cchar) + call s:setup_commands(a:cchar) + + let save_efm=&efm + set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f' + + let lines = ["Entering dir 'dir1/a'", + \ 'habits2.txt:1:Nine Healthy Habits', + \ "Entering dir 'b'", + \ 'habits3.txt:2:0 Hours of television', + \ 'habits2.txt:7:5 Small meals', + \ "Entering dir 'dir1/c'", + \ 'habits4.txt:3:1 Hour of exercise', + \ "Leaving dir 'dir1/c'", + \ "Leaving dir 'dir1/a'", + \ 'habits1.txt:4:2 Liters of water', + \ "Entering dir 'dir2'", + \ 'habits5.txt:5:3 Cups of hot green tea', + \ "Leaving dir 'dir2'" + \] + + Xexpr "" + for l in lines + Xaddexpr l + endfor + + let qf = g:Xgetlist() + + call assert_equal('dir1/a/habits2.txt', bufname(qf[1].bufnr)) + call assert_equal(1, qf[1].lnum) + call assert_equal('dir1/a/b/habits3.txt', bufname(qf[3].bufnr)) + call assert_equal(2, qf[3].lnum) + call assert_equal('dir1/a/habits2.txt', bufname(qf[4].bufnr)) + call assert_equal(7, qf[4].lnum) + call assert_equal('dir1/c/habits4.txt', bufname(qf[6].bufnr)) + call assert_equal(3, qf[6].lnum) + call assert_equal('habits1.txt', bufname(qf[9].bufnr)) + call assert_equal(4, qf[9].lnum) + call assert_equal('dir2/habits5.txt', bufname(qf[11].bufnr)) + call assert_equal(5, qf[11].lnum) + + let &efm=save_efm +endfunction + +" Tests for %D and %X errorformat options +function! Test_efm_dirstack() + " Create the directory stack and files + call mkdir('dir1') + call mkdir('dir1/a') + call mkdir('dir1/a/b') + call mkdir('dir1/c') + call mkdir('dir2') + + let lines = ["Nine Healthy Habits", + \ "0 Hours of television", + \ "1 Hour of exercise", + \ "2 Liters of water", + \ "3 Cups of hot green tea", + \ "4 Short mental breaks", + \ "5 Small meals", + \ "6 AM wake up time", + \ "7 Minutes of laughter", + \ "8 Hours of sleep (at least)", + \ "9 PM end of the day and off to bed" + \ ] + call writefile(lines, 'habits1.txt') + call writefile(lines, 'dir1/a/habits2.txt') + call writefile(lines, 'dir1/a/b/habits3.txt') + call writefile(lines, 'dir1/c/habits4.txt') + call writefile(lines, 'dir2/habits5.txt') + + call s:dir_stack_tests('c') + call s:dir_stack_tests('l') + + call delete('dir1', 'rf') + call delete('dir2', 'rf') + call delete('habits1.txt') +endfunction + +" Tests for invalid error format specifies +function Xinvalid_efm_Tests(cchar) + call s:setup_commands(a:cchar) + + let save_efm = &efm + + set efm=%f:%l:%m,%f:%f:%l:%m + call assert_fails('Xexpr "abc.txt:1:Hello world"', 'E372:') + + set efm=%f:%l:%m,%f:%l:%r:%m + call assert_fails('Xexpr "abc.txt:1:Hello world"', 'E373:') + + set efm=%f:%l:%m,%O:%f:%l:%m + call assert_fails('Xexpr "abc.txt:1:Hello world"', 'E373:') + + set efm=%f:%l:%m,%f:%l:%*[^a-z + call assert_fails('Xexpr "abc.txt:1:Hello world"', 'E374:') + + set efm=%f:%l:%m,%f:%l:%*c + call assert_fails('Xexpr "abc.txt:1:Hello world"', 'E375:') + + set efm=%f:%l:%m,%L%M%N + call assert_fails('Xexpr "abc.txt:1:Hello world"', 'E376:') + + set efm=%f:%l:%m,%f:%l:%m:%R + call assert_fails('Xexpr "abc.txt:1:Hello world"', 'E377:') + + set efm= + call assert_fails('Xexpr "abc.txt:1:Hello world"', 'E378:') + + set efm=%DEntering\ dir\ abc,%f:%l:%m + call assert_fails('Xexpr ["Entering dir abc", "abc.txt:1:Hello world"]', 'E379:') + + let &efm = save_efm +endfunction + +function Test_invalid_efm() + call Xinvalid_efm_Tests('c') + call Xinvalid_efm_Tests('l') +endfunction + +" TODO: +" Add tests for the following formats in 'errorformat' +" %r %O +function! Test_efm2() + let save_efm = &efm + + " Test for %s format in efm + set efm=%f:%s + cexpr 'Xtestfile:Line search text' + let l = getqflist() + call assert_equal(l[0].pattern, '^\VLine search text\$') + call assert_equal(l[0].lnum, 0) + + " Test for %P, %Q and %t format specifiers + let lines=["[Xtestfile1]", + \ "(1,17) error: ';' missing", + \ "(21,2) warning: variable 'z' not defined", + \ "(67,3) error: end of file found before string ended", + \ "", + \ "[Xtestfile2]", + \ "", + \ "[Xtestfile3]", + \ "NEW compiler v1.1", + \ "(2,2) warning: variable 'x' not defined", + \ "(67,3) warning: 's' already defined" + \] + set efm=%+P[%f],(%l\\,%c)%*[\ ]%t%*[^:]:\ %m,%-Q + cexpr "" + for l in lines + caddexpr l + endfor + let l = getqflist() + call assert_equal(9, len(l)) + call assert_equal(21, l[2].lnum) + call assert_equal(2, l[2].col) + call assert_equal('w', l[2].type) + call assert_equal('e', l[3].type) + + " Tests for %E, %C and %Z format specifiers + let lines = ["Error 275", + \ "line 42", + \ "column 3", + \ "' ' expected after '--'" + \] + set efm=%EError\ %n,%Cline\ %l,%Ccolumn\ %c,%Z%m + cgetexpr lines + let l = getqflist() + call assert_equal(275, l[0].nr) + call assert_equal(42, l[0].lnum) + call assert_equal(3, l[0].col) + call assert_equal('E', l[0].type) + call assert_equal("\n' ' expected after '--'", l[0].text) + + " Test for %> + let lines = ["Error in line 147 of foo.c:", + \"unknown variable 'i'" + \] + set efm=unknown\ variable\ %m,%E%>Error\ in\ line\ %l\ of\ %f:,%Z%m + cgetexpr lines + let l = getqflist() + call assert_equal(147, l[0].lnum) + call assert_equal('E', l[0].type) + call assert_equal("\nunknown variable 'i'", l[0].text) + + let &efm = save_efm +endfunction + +function XquickfixChangedByAutocmd(cchar) + call s:setup_commands(a:cchar) + if a:cchar == 'c' + let ErrorNr = 'E925' + function! ReadFunc() + colder + cgetexpr [] + endfunc + else + let ErrorNr = 'E926' + function! ReadFunc() + lolder + lgetexpr [] + endfunc + endif + + augroup testgroup + au! + autocmd BufReadCmd test_changed.txt call ReadFunc() + augroup END + + new | only + let words = [ "a", "b" ] + let qflist = [] + for word in words + call add(qflist, {'filename': 'test_changed.txt'}) + call g:Xsetlist(qflist, ' ') + endfor + call assert_fails('Xrewind', ErrorNr . ':') + + augroup! testgroup +endfunc + +function Test_quickfix_was_changed_by_autocmd() + call XquickfixChangedByAutocmd('c') + call XquickfixChangedByAutocmd('l') +endfunction + +func Test_caddbuffer_to_empty() + helpgr quickfix + call setqflist([], 'r') + cad + try + cn + catch + " number of matches is unknown + call assert_true(v:exception =~ 'E553:') + endtry + quit! +endfunc + +func Test_cgetexpr_works() + " this must not crash Vim + cgetexpr [$x] + lgetexpr [$x] +endfunc + +" Tests for the setqflist() and setloclist() functions +function SetXlistTests(cchar, bnum) + call s:setup_commands(a:cchar) + + call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 1}, + \ {'bufnr': a:bnum, 'lnum': 2}]) + let l = g:Xgetlist() + call assert_equal(2, len(l)) + call assert_equal(2, l[1].lnum) + + Xnext + call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 3}], 'a') + let l = g:Xgetlist() + call assert_equal(3, len(l)) + Xnext + call assert_equal(3, line('.')) + + " Appending entries to the list should not change the cursor position + " in the quickfix window + Xwindow + 1 + call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 4}, + \ {'bufnr': a:bnum, 'lnum': 5}], 'a') + call assert_equal(1, line('.')) + close + + call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 3}, + \ {'bufnr': a:bnum, 'lnum': 4}, + \ {'bufnr': a:bnum, 'lnum': 5}], 'r') + let l = g:Xgetlist() + call assert_equal(3, len(l)) + call assert_equal(5, l[2].lnum) + + call g:Xsetlist([]) + let l = g:Xgetlist() + call assert_equal(0, len(l)) +endfunction + +function Test_setqflist() + new Xtestfile | only + let bnum = bufnr('%') + call setline(1, range(1,5)) + + call SetXlistTests('c', bnum) + call SetXlistTests('l', bnum) + + enew! + call delete('Xtestfile') +endfunction + +function Xlist_empty_middle(cchar) + call s:setup_commands(a:cchar) + + " create three quickfix lists + Xvimgrep Test_ test_quickfix.vim + let testlen = len(g:Xgetlist()) + call assert_true(testlen > 0) + Xvimgrep empty test_quickfix.vim + call assert_true(len(g:Xgetlist()) > 0) + Xvimgrep matches test_quickfix.vim + let matchlen = len(g:Xgetlist()) + call assert_true(matchlen > 0) + Xolder + " make the middle list empty + call g:Xsetlist([], 'r') + call assert_true(len(g:Xgetlist()) == 0) + Xolder + call assert_equal(testlen, len(g:Xgetlist())) + Xnewer + Xnewer + call assert_equal(matchlen, len(g:Xgetlist())) +endfunc + +function Test_setqflist_empty_middle() + call Xlist_empty_middle('c') + call Xlist_empty_middle('l') +endfunction + +function Xlist_empty_older(cchar) + call s:setup_commands(a:cchar) + + " create three quickfix lists + Xvimgrep one test_quickfix.vim + let onelen = len(g:Xgetlist()) + call assert_true(onelen > 0) + Xvimgrep two test_quickfix.vim + let twolen = len(g:Xgetlist()) + call assert_true(twolen > 0) + Xvimgrep three test_quickfix.vim + let threelen = len(g:Xgetlist()) + call assert_true(threelen > 0) + Xolder 2 + " make the first list empty, check the others didn't change + call g:Xsetlist([], 'r') + call assert_true(len(g:Xgetlist()) == 0) + Xnewer + call assert_equal(twolen, len(g:Xgetlist())) + Xnewer + call assert_equal(threelen, len(g:Xgetlist())) +endfunction + +function Test_setqflist_empty_older() + call Xlist_empty_older('c') + call Xlist_empty_older('l') +endfunction + +function! XquickfixSetListWithAct(cchar) + call s:setup_commands(a:cchar) + + let list1 = [{'filename': 'fnameA', 'text': 'A'}, + \ {'filename': 'fnameB', 'text': 'B'}] + let list2 = [{'filename': 'fnameC', 'text': 'C'}, + \ {'filename': 'fnameD', 'text': 'D'}, + \ {'filename': 'fnameE', 'text': 'E'}] + + " {action} is unspecified. Same as specifing ' '. + new | only + silent! Xnewer 99 + call g:Xsetlist(list1) + call g:Xsetlist(list2) + let li = g:Xgetlist() + call assert_equal(3, len(li)) + call assert_equal('C', li[0]['text']) + call assert_equal('D', li[1]['text']) + call assert_equal('E', li[2]['text']) + silent! Xolder + let li = g:Xgetlist() + call assert_equal(2, len(li)) + call assert_equal('A', li[0]['text']) + call assert_equal('B', li[1]['text']) + + " {action} is specified ' '. + new | only + silent! Xnewer 99 + call g:Xsetlist(list1) + call g:Xsetlist(list2, ' ') + let li = g:Xgetlist() + call assert_equal(3, len(li)) + call assert_equal('C', li[0]['text']) + call assert_equal('D', li[1]['text']) + call assert_equal('E', li[2]['text']) + silent! Xolder + let li = g:Xgetlist() + call assert_equal(2, len(li)) + call assert_equal('A', li[0]['text']) + call assert_equal('B', li[1]['text']) + + " {action} is specified 'a'. + new | only + silent! Xnewer 99 + call g:Xsetlist(list1) + call g:Xsetlist(list2, 'a') + let li = g:Xgetlist() + call assert_equal(5, len(li)) + call assert_equal('A', li[0]['text']) + call assert_equal('B', li[1]['text']) + call assert_equal('C', li[2]['text']) + call assert_equal('D', li[3]['text']) + call assert_equal('E', li[4]['text']) + + " {action} is specified 'r'. + new | only + silent! Xnewer 99 + call g:Xsetlist(list1) + call g:Xsetlist(list2, 'r') + let li = g:Xgetlist() + call assert_equal(3, len(li)) + call assert_equal('C', li[0]['text']) + call assert_equal('D', li[1]['text']) + call assert_equal('E', li[2]['text']) + + " Test for wrong value. + new | only + call assert_fails("call g:Xsetlist(0)", 'E714:') + call assert_fails("call g:Xsetlist(list1, '')", 'E927:') + call assert_fails("call g:Xsetlist(list1, 'aa')", 'E927:') + call assert_fails("call g:Xsetlist(list1, ' a')", 'E927:') + call assert_fails("call g:Xsetlist(list1, 0)", 'E928:') +endfunc + +function Test_quickfix_set_list_with_act() + call XquickfixSetListWithAct('c') + call XquickfixSetListWithAct('l') +endfunction + +function XLongLinesTests(cchar) + let l = g:Xgetlist() + + call assert_equal(4, len(l)) + call assert_equal(1, l[0].lnum) + call assert_equal(1, l[0].col) + call assert_equal(1975, len(l[0].text)) + call assert_equal(2, l[1].lnum) + call assert_equal(1, l[1].col) + call assert_equal(4070, len(l[1].text)) + call assert_equal(3, l[2].lnum) + call assert_equal(1, l[2].col) + call assert_equal(4070, len(l[2].text)) + call assert_equal(4, l[3].lnum) + call assert_equal(1, l[3].col) + call assert_equal(10, len(l[3].text)) + + call g:Xsetlist([], 'r') +endfunction + +function s:long_lines_tests(cchar) + call s:setup_commands(a:cchar) + + let testfile = 'samples/quickfix.txt' + + " file + exe 'Xgetfile' testfile + call XLongLinesTests(a:cchar) + + " list + Xexpr readfile(testfile) + call XLongLinesTests(a:cchar) + + " string + Xexpr join(readfile(testfile), "\n") + call XLongLinesTests(a:cchar) + + " buffer + exe 'edit' testfile + exe 'Xbuffer' bufnr('%') + call XLongLinesTests(a:cchar) +endfunction + +function Test_long_lines() + call s:long_lines_tests('c') + call s:long_lines_tests('l') +endfunction + +function! s:create_test_file(filename) + let l = [] + for i in range(1, 20) + call add(l, 'Line' . i) + endfor + call writefile(l, a:filename) +endfunction + +function! Test_switchbuf() + call s:create_test_file('Xqftestfile1') + call s:create_test_file('Xqftestfile2') + call s:create_test_file('Xqftestfile3') + + new | only + edit Xqftestfile1 + let file1_winid = win_getid() + new Xqftestfile2 + let file2_winid = win_getid() + cgetexpr ['Xqftestfile1:5:Line5', + \ 'Xqftestfile1:6:Line6', + \ 'Xqftestfile2:10:Line10', + \ 'Xqftestfile2:11:Line11', + \ 'Xqftestfile3:15:Line15', + \ 'Xqftestfile3:16:Line16'] + + new + let winid = win_getid() + cfirst | cnext + call assert_equal(winid, win_getid()) + cnext | cnext + call assert_equal(winid, win_getid()) + cnext | cnext + call assert_equal(winid, win_getid()) + enew + + set switchbuf=useopen + cfirst | cnext + call assert_equal(file1_winid, win_getid()) + cnext | cnext + call assert_equal(file2_winid, win_getid()) + cnext | cnext + call assert_equal(file2_winid, win_getid()) + + enew | only + set switchbuf=usetab + tabedit Xqftestfile1 + tabedit Xqftestfile2 + tabfirst + cfirst | cnext + call assert_equal(2, tabpagenr()) + cnext | cnext + call assert_equal(3, tabpagenr()) + cnext | cnext + call assert_equal(3, tabpagenr()) + tabfirst | tabonly | enew + + set switchbuf=split + cfirst | cnext + call assert_equal(1, winnr('$')) + cnext | cnext + call assert_equal(2, winnr('$')) + cnext | cnext + call assert_equal(3, winnr('$')) + enew | only + + set switchbuf=newtab + cfirst | cnext + call assert_equal(1, tabpagenr('$')) + cnext | cnext + call assert_equal(2, tabpagenr('$')) + cnext | cnext + call assert_equal(3, tabpagenr('$')) + tabfirst | enew | tabonly | only + + set switchbuf= + edit Xqftestfile1 + let file1_winid = win_getid() + new Xqftestfile2 + let file2_winid = win_getid() + copen + exe "normal 1G\<CR>" + call assert_equal(file1_winid, win_getid()) + copen + exe "normal 3G\<CR>" + call assert_equal(file2_winid, win_getid()) + copen | only + exe "normal 5G\<CR>" + call assert_equal(2, winnr('$')) + call assert_equal(1, bufwinnr('Xqftestfile3')) + + enew | only + + call delete('Xqftestfile1') + call delete('Xqftestfile2') + call delete('Xqftestfile3') +endfunction + +function! Xadjust_qflnum(cchar) + call s:setup_commands(a:cchar) + + enew | only + + call s:create_test_file('Xqftestfile') + edit Xqftestfile + + Xgetexpr ['Xqftestfile:5:Line5', + \ 'Xqftestfile:10:Line10', + \ 'Xqftestfile:15:Line15', + \ 'Xqftestfile:20:Line20'] + + 6,14delete + call append(6, ['Buffer', 'Window']) + + let l = g:Xgetlist() + + call assert_equal(5, l[0].lnum) + call assert_equal(6, l[2].lnum) + call assert_equal(13, l[3].lnum) + + enew! + call delete('Xqftestfile') +endfunction + +function! Test_adjust_lnum() + call Xadjust_qflnum('c') + call Xadjust_qflnum('l') +endfunction + +" Tests for the :grep/:lgrep and :grepadd/:lgrepadd commands +function! s:test_xgrep(cchar) + call s:setup_commands(a:cchar) + + " The following lines are used for the grep test. Don't remove. + " Grep_Test_Text: Match 1 + " Grep_Test_Text: Match 2 + " GrepAdd_Test_Text: Match 1 + " GrepAdd_Test_Text: Match 2 + enew! | only + set makeef&vim + silent Xgrep Grep_Test_Text: test_quickfix.vim + call assert_true(len(g:Xgetlist()) == 3) + Xopen + call assert_true(w:quickfix_title =~ '^:grep') + Xclose + enew + set makeef=Temp_File_## + silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim + call assert_true(len(g:Xgetlist()) == 6) +endfunction + +function! Test_grep() + if !has('unix') + " The grepprg may not be set on non-Unix systems + return + endif + + call s:test_xgrep('c') + call s:test_xgrep('l') +endfunction + +function! Test_two_windows() + " Use one 'errorformat' for two windows. Add an expression to each of them, + " make sure they each keep their own state. + set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f' + 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') + + new one + let one_id = win_getid() + lexpr "" + new two + let two_id = win_getid() + lexpr "" + + laddexpr "Entering dir 'Xtwo/a'" + call win_gotoid(one_id) + laddexpr "Entering dir 'Xone/a'" + call win_gotoid(two_id) + laddexpr 'two.txt:5:two two two' + call win_gotoid(one_id) + laddexpr 'one.txt:3:one one one' + + let loc_one = getloclist(one_id) +echo string(loc_one) + call assert_equal('Xone/a/one.txt', bufname(loc_one[1].bufnr)) + call assert_equal(3, loc_one[1].lnum) + + let loc_two = getloclist(two_id) +echo string(loc_two) + call assert_equal('Xtwo/a/two.txt', bufname(loc_two[1].bufnr)) + call assert_equal(5, loc_two[1].lnum) + + call win_gotoid(one_id) + bwipe! + call win_gotoid(two_id) + bwipe! + call delete('Xone', 'rf') + call delete('Xtwo', 'rf') +endfunc + +function XbottomTests(cchar) + call s:setup_commands(a:cchar) + + call g:Xsetlist([{'filename': 'foo', 'lnum': 42}]) + Xopen + let wid = win_getid() + call assert_equal(1, line('.')) + wincmd w + call g:Xsetlist([{'filename': 'var', 'lnum': 24}], 'a') + Xbottom + call win_gotoid(wid) + call assert_equal(2, line('.')) + Xclose +endfunc + +" Tests for the :cbottom and :lbottom commands +function Test_cbottom() + call XbottomTests('c') + call XbottomTests('l') +endfunction diff --git a/src/nvim/version.c b/src/nvim/version.c index ed1d7448ea..512d52cbc4 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -430,7 +430,7 @@ static int included_patches[] = { 2013, 2012, 2011, - // 2010, + 2010, // 2009, // 2008, 2007, @@ -443,7 +443,7 @@ static int included_patches[] = { // 2000, // 1999, // 1998 NA - // 1997, + 1997, // 1996, // 1995 NA // 1994, @@ -456,11 +456,11 @@ static int included_patches[] = { // 1987 NA // 1986, // 1985 NA - // 1984, + 1984, // 1983 NA // 1982 NA // 1981, - // 1980, + 1980, // 1979, // 1978, // 1977, @@ -474,9 +474,9 @@ static int included_patches[] = { // 1969 NA // 1968, 1967, - // 1966, + 1966, // 1965 NA - // 1964, + 1964, // 1963 NA // 1962, 1961, @@ -487,11 +487,11 @@ static int included_patches[] = { 1956, // 1955, // 1954, - // 1953, + 1953, 1952, // 1951 NA - // 1950, - // 1949, + 1950, + 1949, // 1948, // 1947 NA // 1946 NA @@ -499,11 +499,11 @@ static int included_patches[] = { // 1944 NA // 1943 NA // 1942 NA - // 1941, + 1941, // 1940, // 1939 NA // 1938 NA - // 1937, + 1937, // 1936, // 1935 NA // 1934 NA @@ -528,7 +528,7 @@ static int included_patches[] = { // 1915 NA // 1914, 1913, - // 1912, + 1912, // 1911, // 1910, 1909, @@ -556,10 +556,10 @@ static int included_patches[] = { // 1887 NA // 1886 NA // 1885 NA - // 1884, + 1884, // 1883 NA - // 1882, - // 1881, + 1882, + 1881, // 1880 NA // 1879 NA // 1878 NA @@ -569,7 +569,7 @@ static int included_patches[] = { // 1874 NA // 1873 NA // 1872 NA - // 1871, + 1871, // 1870 NA // 1869 NA // 1868, @@ -599,7 +599,7 @@ static int included_patches[] = { // 1844, // 1843 NA 1842, - // 1841, + 1841, 1840, // 1839, // 1838, @@ -616,7 +616,7 @@ static int included_patches[] = { // 1826 NA // 1825 NA // 1824 NA - // 1823, + 1823, // 1822 NA 1821, 1820, @@ -624,9 +624,9 @@ static int included_patches[] = { 1818, // 1817 NA 1816, - // 1815, + 1815, // 1814 NA - // 1813, + 1813, // 1812, // 1811 NA // 1810 NA @@ -637,7 +637,7 @@ static int included_patches[] = { // 1805 NA // 1804 NA // 1803 NA - // 1802, + 1802, // 1801 NA // 1800 NA 1799, @@ -672,7 +672,7 @@ static int included_patches[] = { // 1771 NA // 1770 NA // 1769, - // 1768, + 1768, // 1767 NA // 1766 NA 1765, @@ -689,7 +689,7 @@ static int included_patches[] = { 1754, 1753, // 1753, - // 1752, + 1752, 1751, // 1750 NA // 1749 NA diff --git a/test/functional/legacy/quickfix_spec.lua b/test/functional/legacy/quickfix_spec.lua deleted file mode 100644 index 5787ff8ff3..0000000000 --- a/test/functional/legacy/quickfix_spec.lua +++ /dev/null @@ -1,683 +0,0 @@ --- Test for the quickfix commands. - -local helpers = require('test.functional.helpers')(after_each) -local source, clear = helpers.source, helpers.clear -local eq, nvim, call = helpers.eq, helpers.meths, helpers.call -local eval = helpers.eval -local execute = helpers.execute - -local function expected_empty() - eq({}, nvim.get_vvar('errors')) -end - -describe('helpgrep', function() - before_each(function() - clear() - - source([[ - " Tests for the :clist and :llist commands - function XlistTests(cchar) - let Xlist = a:cchar . 'list' - let Xgetexpr = a:cchar . 'getexpr' - - " With an empty list, command should return error - exe Xgetexpr . ' []' - exe 'silent! ' . Xlist - call assert_true(v:errmsg ==# 'E42: No Errors') - - " Populate the list and then try - exe Xgetexpr . " ['non-error 1', 'Xtestfile1:1:3:Line1', - \ 'non-error 2', 'Xtestfile2:2:2:Line2', - \ 'non-error 3', 'Xtestfile3:3:1:Line3']" - - " List only valid entries - redir => result - exe 'silent ' . Xlist - redir END - let l = split(result, "\n") - call assert_equal([' 2 Xtestfile1:1 col 3: Line1', - \ ' 4 Xtestfile2:2 col 2: Line2', - \ ' 6 Xtestfile3:3 col 1: Line3'], l) - - " List all the entries - redir => result - exe 'silent ' . Xlist . "!" - redir END - let l = split(result, "\n") - call assert_equal([' 1: non-error 1', ' 2 Xtestfile1:1 col 3: Line1', - \ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2', - \ ' 5: non-error 3', ' 6 Xtestfile3:3 col 1: Line3'], l) - - " List a range of errors - redir => result - exe 'silent '. Xlist . " 3,6" - redir END - let l = split(result, "\n") - call assert_equal([' 4 Xtestfile2:2 col 2: Line2', - \ ' 6 Xtestfile3:3 col 1: Line3'], l) - - redir => result - exe 'silent ' . Xlist . "! 3,4" - redir END - let l = split(result, "\n") - call assert_equal([' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l) - - redir => result - exe 'silent ' . Xlist . " -6,-4" - redir END - let l = split(result, "\n") - call assert_equal([' 2 Xtestfile1:1 col 3: Line1'], l) - - redir => result - exe 'silent ' . Xlist . "! -5,-3" - redir END - let l = split(result, "\n") - call assert_equal([' 2 Xtestfile1:1 col 3: Line1', - \ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l) - endfunction - - " Tests for the :colder, :cnewer, :lolder and :lnewer commands - " Note that this test assumes that a quickfix/location list is - " already set by the caller - function XageTests(cchar) - let Xolder = a:cchar . 'older' - let Xnewer = a:cchar . 'newer' - let Xgetexpr = a:cchar . 'getexpr' - if a:cchar == 'c' - let Xgetlist = 'getqflist()' - else - let Xgetlist = 'getloclist(0)' - endif - - " Jumping to a non existent list should return error - exe 'silent! ' . Xolder . ' 99' - call assert_true(v:errmsg ==# 'E380: At bottom of quickfix stack') - - exe 'silent! ' . Xnewer . ' 99' - call assert_true(v:errmsg ==# 'E381: At top of quickfix stack') - - " Add three quickfix/location lists - exe Xgetexpr . " ['Xtestfile1:1:3:Line1']" - exe Xgetexpr . " ['Xtestfile2:2:2:Line2']" - exe Xgetexpr . " ['Xtestfile3:3:1:Line3']" - - " Go back two lists - exe Xolder - exe 'let l = ' . Xgetlist - call assert_equal('Line2', l[0].text) - - " Go forward two lists - exe Xnewer - exe 'let l = ' . Xgetlist - call assert_equal('Line3', l[0].text) - - " Test for the optional count argument - exe Xolder . ' 2' - exe 'let l = ' . Xgetlist - call assert_equal('Line1', l[0].text) - - exe Xnewer . ' 2' - exe 'let l = ' . Xgetlist - call assert_equal('Line3', l[0].text) - endfunction - - " Tests for the :cwindow, :lwindow :cclose, :lclose, :copen and :lopen - " commands - function XwindowTests(cchar) - let Xwindow = a:cchar . 'window' - let Xclose = a:cchar . 'close' - let Xopen = a:cchar . 'open' - let Xgetexpr = a:cchar . 'getexpr' - - " Create a list with no valid entries - exe Xgetexpr . " ['non-error 1', 'non-error 2', 'non-error 3']" - - " Quickfix/Location window should not open with no valid errors - exe Xwindow - call assert_true(winnr('$') == 1) - - " Create a list with valid entries - exe Xgetexpr . " ['Xtestfile1:1:3:Line1', 'Xtestfile2:2:2:Line2', - \ 'Xtestfile3:3:1:Line3']" - - " Open the window - exe Xwindow - call assert_true(winnr('$') == 2 && winnr() == 2 && - \ getline('.') ==# 'Xtestfile1|1 col 3| Line1') - - " Close the window - exe Xclose - call assert_true(winnr('$') == 1) - - " Create a list with no valid entries - exe Xgetexpr . " ['non-error 1', 'non-error 2', 'non-error 3']" - - " Open the window - exe Xopen . ' 5' - call assert_true(winnr('$') == 2 && getline('.') ==# '|| non-error 1' - \ && winheight('.') == 5) - - " Opening the window again, should move the cursor to that window - wincmd t - exe Xopen . ' 7' - call assert_true(winnr('$') == 2 && winnr() == 2 && - \ winheight('.') == 7 && - \ getline('.') ==# '|| non-error 1') - - - " Calling cwindow should close the quickfix window with no valid errors - exe Xwindow - call assert_true(winnr('$') == 1) - endfunction - - " Tests for the :cfile, :lfile, :caddfile, :laddfile, :cgetfile and :lgetfile - " commands. - function XfileTests(cchar) - let Xfile = a:cchar . 'file' - let Xgetfile = a:cchar . 'getfile' - let Xaddfile = a:cchar . 'addfile' - if a:cchar == 'c' - let Xgetlist = 'getqflist()' - else - let Xgetlist = 'getloclist(0)' - endif - - call writefile(['Xtestfile1:700:10:Line 700', - \ 'Xtestfile2:800:15:Line 800'], 'Xqftestfile1') - - enew! - exe Xfile . ' Xqftestfile1' - exe 'let l = ' . Xgetlist - call assert_true(len(l) == 2 && - \ l[0].lnum == 700 && l[0].col == 10 && l[0].text ==# 'Line 700' && - \ l[1].lnum == 800 && l[1].col == 15 && l[1].text ==# 'Line 800') - - " Run cfile/lfile from a modified buffer - enew! - silent! put ='Quickfix' - exe 'silent! ' . Xfile . ' Xqftestfile1' - call assert_true(v:errmsg ==# 'E37: No write since last change (add ! to override)') - - call writefile(['Xtestfile3:900:30:Line 900'], 'Xqftestfile1') - exe Xaddfile . ' Xqftestfile1' - exe 'let l = ' . Xgetlist - call assert_true(len(l) == 3 && - \ l[2].lnum == 900 && l[2].col == 30 && l[2].text ==# 'Line 900') - - call writefile(['Xtestfile1:222:77:Line 222', - \ 'Xtestfile2:333:88:Line 333'], 'Xqftestfile1') - - enew! - exe Xgetfile . ' Xqftestfile1' - exe 'let l = ' . Xgetlist - call assert_true(len(l) == 2 && - \ l[0].lnum == 222 && l[0].col == 77 && l[0].text ==# 'Line 222' && - \ l[1].lnum == 333 && l[1].col == 88 && l[1].text ==# 'Line 333') - - call delete('Xqftestfile1') - endfunction - - " Tests for the :cbuffer, :lbuffer, :caddbuffer, :laddbuffer, :cgetbuffer and - " :lgetbuffer commands. - function XbufferTests(cchar) - let Xbuffer = a:cchar . 'buffer' - let Xgetbuffer = a:cchar . 'getbuffer' - let Xaddbuffer = a:cchar . 'addbuffer' - if a:cchar == 'c' - let Xgetlist = 'getqflist()' - else - let Xgetlist = 'getloclist(0)' - endif - - enew! - silent! call setline(1, ['Xtestfile7:700:10:Line 700', - \ 'Xtestfile8:800:15:Line 800']) - exe Xbuffer . "!" - exe 'let l = ' . Xgetlist - call assert_true(len(l) == 2 && - \ l[0].lnum == 700 && l[0].col == 10 && l[0].text ==# 'Line 700' && - \ l[1].lnum == 800 && l[1].col == 15 && l[1].text ==# 'Line 800') - - enew! - silent! call setline(1, ['Xtestfile9:900:55:Line 900', - \ 'Xtestfile10:950:66:Line 950']) - exe Xgetbuffer - exe 'let l = ' . Xgetlist - call assert_true(len(l) == 2 && - \ l[0].lnum == 900 && l[0].col == 55 && l[0].text ==# 'Line 900' && - \ l[1].lnum == 950 && l[1].col == 66 && l[1].text ==# 'Line 950') - - enew! - silent! call setline(1, ['Xtestfile11:700:20:Line 700', - \ 'Xtestfile12:750:25:Line 750']) - exe Xaddbuffer - exe 'let l = ' . Xgetlist - call assert_true(len(l) == 4 && - \ l[1].lnum == 950 && l[1].col == 66 && l[1].text ==# 'Line 950' && - \ l[2].lnum == 700 && l[2].col == 20 && l[2].text ==# 'Line 700' && - \ l[3].lnum == 750 && l[3].col == 25 && l[3].text ==# 'Line 750') - - endfunction - - function Test_locationlist_curwin_was_closed() - augroup testgroup - au! - autocmd BufReadCmd t call R(expand("<amatch>")) - augroup END - - function! R(n) - quit - endfunc - - new - let q = [] - call add(q, {'filename': 't' }) - call setloclist(0, q) - call assert_fails('lrewind', 'E924:') - - augroup! testgroup - endfunction - - " This will test for problems in quickfix: - " A. incorrectly copying location lists which caused the location list to show - " a different name than the file that was actually being displayed. - " B. not reusing the window for which the location list window is opened but - " instead creating new windows. - " C. make sure that the location list window is not reused instead of the - " window it belongs to. - " - " Set up the test environment: - function! ReadTestProtocol(name) - let base = substitute(a:name, '\v^test://(.*)%(\.[^.]+)?', '\1', '') - let word = substitute(base, '\v(.*)\..*', '\1', '') - - setl modifiable - setl noreadonly - setl noswapfile - setl bufhidden=delete - %del _ - " For problem 2: - " 'buftype' has to be set to reproduce the constant opening of new windows - setl buftype=nofile - - call setline(1, word) - - setl nomodified - setl nomodifiable - setl readonly - exe 'doautocmd BufRead ' . substitute(a:name, '\v^test://(.*)', '\1', '') - endfunction - - function Test_locationlist() - enew - - augroup testgroup - au! - autocmd BufReadCmd test://* call ReadTestProtocol(expand("<amatch>")) - augroup END - - let words = [ "foo", "bar", "baz", "quux", "shmoo", "spam", "eggs" ] - - let qflist = [] - for word in words - call add(qflist, {'filename': 'test://' . word . '.txt', 'text': 'file ' . word . '.txt', }) - " NOTE: problem 1: - " intentionally not setting 'lnum' so that the quickfix entries are not - " valid - call setloclist(0, qflist, ' ') - endfor - - " Test A - lrewind - enew - lopen - lnext - lnext - lnext - lnext - vert split - wincmd L - lopen - wincmd p - lnext - let fileName = expand("%") - wincmd p - let locationListFileName = substitute(getline(line('.')), '\([^|]*\)|.*', '\1', '') - let fileName = substitute(fileName, '\\', '/', 'g') - let locationListFileName = substitute(locationListFileName, '\\', '/', 'g') - call assert_equal("test://bar.txt", fileName) - call assert_equal("test://bar.txt", locationListFileName) - - wincmd n | only - - " Test B: - lrewind - lopen - 2 - exe "normal \<CR>" - wincmd p - 3 - exe "normal \<CR>" - wincmd p - 4 - exe "normal \<CR>" - call assert_equal(2, winnr('$')) - wincmd n | only - - " Test C: - lrewind - lopen - " Let's move the location list window to the top to check whether it (the - " first window found) will be reused when we try to open new windows: - wincmd K - 2 - exe "normal \<CR>" - wincmd p - 3 - exe "normal \<CR>" - wincmd p - 4 - exe "normal \<CR>" - 1wincmd w - call assert_equal('quickfix', &buftype) - 2wincmd w - let bufferName = expand("%") - let bufferName = substitute(bufferName, '\\', '/', 'g') - call assert_equal('test://quux.txt', bufferName) - - wincmd n | only - - augroup! testgroup - endfunction - - func Test_vimgreptitle() - augroup QfBufWinEnter - au! - au BufWinEnter * :let g:a=get(w:, 'quickfix_title', 'NONE') - augroup END - try - vimgrep /pattern/j file - catch /E480/ - endtry - copen - call assert_equal(': vimgrep /pattern/j file', g:a) - augroup QfBufWinEnter - au! - augroup END - augroup! QfBufWinEnter - endfunc - - function XquickfixChangedByAutocmd(cchar) - let Xolder = a:cchar . 'older' - let Xgetexpr = a:cchar . 'getexpr' - let Xrewind = a:cchar . 'rewind' - if a:cchar == 'c' - let Xsetlist = 'setqflist(' - let ErrorNr = 'E925' - function! ReadFunc() - colder - cgetexpr [] - endfunc - else - let Xsetlist = 'setloclist(0,' - let ErrorNr = 'E926' - function! ReadFunc() - lolder - lgetexpr [] - endfunc - endif - - augroup testgroup - au! - autocmd BufReadCmd t call ReadFunc() - augroup END - - bwipe! - let words = [ "a", "b" ] - let qflist = [] - for word in words - call add(qflist, {'filename': 't'}) - exec "call " . Xsetlist . "qflist, '')" - endfor - exec "call assert_fails('" . Xrewind . "', '" . ErrorNr . ":')" - - augroup! testgroup - endfunc - - func Test_caddbuffer_to_empty() - helpgr quickfix - call setqflist([], 'r') - cad - try - silent cn - catch - " number of matches is unknown - call assert_true(v:exception =~ 'E553:') - endtry - quit! - endfunc - ]]) - end) - - it('copen/cclose work', function() - source([[ - helpgrep quickfix - copen - " This wipes out the buffer, make sure that doesn't cause trouble. - cclose - ]]) - end) - - it('clist/llist work', function() - call('XlistTests', 'c') - expected_empty() - call('XlistTests', 'l') - expected_empty() - end) - - it('colder/cnewer and lolder/lnewer work', function() - local list = {{bufnr = 1, lnum = 1}} - call('setqflist', list) - call('XageTests', 'c') - expected_empty() - - call('setloclist', 0, list) - call('XageTests', 'l') - expected_empty() - end) - - it('quickfix/location list window commands work', function() - call('XwindowTests', 'c') - expected_empty() - call('XwindowTests', 'l') - expected_empty() - end) - - it('quickfix/location list file commands work', function() - call('XfileTests', 'c') - expected_empty() - call('XfileTests', 'l') - expected_empty() - end) - - it('quickfix/location list buffer commands work', function() - call('XbufferTests', 'c') - expected_empty() - call('XbufferTests', 'l') - expected_empty() - end) - - it('autocommands triggered by quickfix can get title', function() - execute('au FileType qf let g:foo = get(w:, "quickfix_title", "NONE")') - execute('call setqflist([])') - execute('copen') - eq(':setqflist()', eval('g:foo')) - end) - - it('does not truncate quickfix title', function() - call('Test_vimgreptitle') - expected_empty() - end) - - it('errors when an autocommand closes the location list\'s window', function() - call('Test_locationlist_curwin_was_closed') - expected_empty() - end) - - it('checks locationlist protocol read', function() - call('Test_locationlist') - expected_empty() - end) - - it('is changed by autocmd', function() - call('XquickfixChangedByAutocmd', 'c') - expected_empty() - call('XquickfixChangedByAutocmd', 'l') - expected_empty() - end) - - it('does not crash after using caddbuffer with an empty qf list', function() - call('Test_caddbuffer_to_empty') - expected_empty() - end) - - it('cgetexpr does not crash with a NULL element in a list', function() - execute('cgetexpr [$x]') - -- Still alive? - eq(2, eval('1+1')) - end) -end) - -describe('errorformat', function() - before_each(function() - clear() - - source([[ - " More tests for 'errorformat' - function! Test_efm1() - if !has('unix') - " The 'errorformat' setting is different on non-Unix systems. - " This test works only on Unix-like systems. - return - endif - - let l = [ - \ '"Xtestfile", line 4.12: 1506-045 (S) Undeclared identifier fd_set.', - \ '"Xtestfile", line 6 col 19; this is an error', - \ 'gcc -c -DHAVE_CONFIsing-prototypes -I/usr/X11R6/include version.c', - \ 'Xtestfile:9: parse error before `asd''', - \ 'make: *** [vim] Error 1', - \ 'in file "Xtestfile" linenr 10: there is an error', - \ '', - \ '2 returned', - \ '"Xtestfile", line 11 col 1; this is an error', - \ '"Xtestfile", line 12 col 2; this is another error', - \ '"Xtestfile", line 14:10; this is an error in column 10', - \ '=Xtestfile=, line 15:10; this is another error, but in vcol 10 this time', - \ '"Xtestfile", linenr 16: yet another problem', - \ 'Error in "Xtestfile" at line 17:', - \ 'x should be a dot', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 17', - \ ' ^', - \ 'Error in "Xtestfile" at line 18:', - \ 'x should be a dot', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 18', - \ '.............^', - \ 'Error in "Xtestfile" at line 19:', - \ 'x should be a dot', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 19', - \ '--------------^', - \ 'Error in "Xtestfile" at line 20:', - \ 'x should be a dot', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 20', - \ ' ^', - \ '', - \ 'Does anyone know what is the problem and how to correction it?', - \ '"Xtestfile", line 21 col 9: What is the title of the quickfix window?', - \ '"Xtestfile", line 22 col 9: What is the title of the quickfix window?' - \ ] - - call writefile(l, 'Xerrorfile1') - call writefile(l[:-2], 'Xerrorfile2') - - let m = [ - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 2', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 3', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 4', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 5', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 6', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 7', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 8', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 9', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 10', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 11', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 12', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 13', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 14', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 15', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 16', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 17', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 18', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 19', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 20', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 21', - \ ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 22' - \ ] - call writefile(m, 'Xtestfile') - - let save_efm = &efm - set efm+==%f=\\,\ line\ %l%*\\D%v%*[^\ ]\ %m - set efm^=%AError\ in\ \"%f\"\ at\ line\ %l:,%Z%p^,%C%m - - exe 'cf Xerrorfile2' - clast - copen - call assert_equal(':cf Xerrorfile2', w:quickfix_title) - wincmd p - - exe 'cf Xerrorfile1' - call assert_equal([4, 12], [line('.'), col('.')]) - cn - call assert_equal([6, 19], [line('.'), col('.')]) - cn - call assert_equal([9, 2], [line('.'), col('.')]) - cn - call assert_equal([10, 2], [line('.'), col('.')]) - cn - call assert_equal([11, 1], [line('.'), col('.')]) - cn - call assert_equal([12, 2], [line('.'), col('.')]) - cn - call assert_equal([14, 10], [line('.'), col('.')]) - cn - call assert_equal([15, 3, 10], [line('.'), col('.'), virtcol('.')]) - cn - call assert_equal([16, 2], [line('.'), col('.')]) - cn - call assert_equal([17, 6], [line('.'), col('.')]) - cn - call assert_equal([18, 7], [line('.'), col('.')]) - cn - call assert_equal([19, 8], [line('.'), col('.')]) - cn - call assert_equal([20, 9], [line('.'), col('.')]) - clast - cprev - cprev - wincmd w - call assert_equal(':cf Xerrorfile1', w:quickfix_title) - wincmd p - - let &efm = save_efm - call delete('Xerrorfile1') - call delete('Xerrorfile2') - call delete('Xtestfile') - endfunction - ]]) - end) - - it('works', function() - call('Test_efm1') - expected_empty() - end) -end) diff --git a/test/functional/viml/errorlist_spec.lua b/test/functional/viml/errorlist_spec.lua index 56d08771b7..f889ca9adc 100644 --- a/test/functional/viml/errorlist_spec.lua +++ b/test/functional/viml/errorlist_spec.lua @@ -18,9 +18,9 @@ describe('setqflist()', function() end) it('requires a string for {action}', function() - eq('Vim(call):E114: String required', exc_exec('call setqflist([], 5)')) - eq('Vim(call):E114: String required', exc_exec('call setqflist([], [])')) - eq('Vim(call):E114: String required', exc_exec('call setqflist([], {})')) + eq('Vim(call):E928: String required', exc_exec('call setqflist([], 5)')) + eq('Vim(call):E928: String required', exc_exec('call setqflist([], [])')) + eq('Vim(call):E928: String required', exc_exec('call setqflist([], {})')) end) it('sets w:quickfix_title', function() @@ -56,9 +56,9 @@ describe('setloclist()', function() end) it('requires a string for {action}', function() - eq('Vim(call):E114: String required', exc_exec('call setloclist(0, [], 5)')) - eq('Vim(call):E114: String required', exc_exec('call setloclist(0, [], [])')) - eq('Vim(call):E114: String required', exc_exec('call setloclist(0, [], {})')) + eq('Vim(call):E928: String required', exc_exec('call setloclist(0, [], 5)')) + eq('Vim(call):E928: String required', exc_exec('call setloclist(0, [], [])')) + eq('Vim(call):E928: String required', exc_exec('call setloclist(0, [], {})')) end) it('sets w:quickfix_title for the correct window', function() |