From e89efe75f9fd872e2c7323ea525b35af90e8afa0 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 12 Dec 2016 10:56:00 -0500 Subject: vim-patch:7.4.1752 Problem: When adding to the quickfix list the current position is reset. Solution: Do not reset the position when not needed. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/c1808d5822ed9534ef7f0fe509b15bee92a5cc28 --- src/nvim/quickfix.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'src/nvim/quickfix.c') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index f0d77f9e2b..b97530b700 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -786,7 +786,7 @@ qf_init_end: xfree(pattern); xfree(fmtstr); - qf_update_buffer(qi); + qf_update_buffer(qi, true); return retval; } @@ -1927,7 +1927,7 @@ 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); + qf_update_buffer(qi, true); } /* @@ -2314,7 +2314,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, bool update_cursor) { buf_T *buf; win_T *win; @@ -2337,8 +2337,9 @@ static void qf_update_buffer(qf_info_T *qi) /* restore curwin/curbuf and a few other things */ aucmd_restbuf(&aco); - - (void)qf_win_pos_update(qi, 0); + if (update_cursor) { + (void)qf_win_pos_update(qi, 0); + } } } @@ -3240,7 +3241,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, true); if (au_name != NULL) apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, @@ -3624,12 +3625,15 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) 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 (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, (action != 'a')); return retval; } @@ -3885,7 +3889,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, true); if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, -- cgit From 39faa56bce66796f731ec339301acc3d303c08e9 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 12 Dec 2016 15:04:44 -0500 Subject: vim-patch:7.4.1802 Problem: Quickfix doesn't handle long lines well, they are split. Solution: Drop characters after a limit. (Anton Lindqvist) https://github.com/vim/vim/commit/6be8c8e165204b8aa4eeb8a52be87a58d8b41b9e --- src/nvim/quickfix.c | 210 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 175 insertions(+), 35 deletions(-) (limited to 'src/nvim/quickfix.c') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index b97530b700..3c59fcef03 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -168,6 +168,9 @@ qf_init ( qf_title); } +// Maximum number of bytes allowed per line while reading an errorfile. +static const size_t LINE_MAXLEN = 4096; + /* * Read the errorfile "efile" into memory, line by line, building the error * list. @@ -192,8 +195,15 @@ 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; + char_u *linebuf; + size_t linelen; + bool discard; int col = 0; bool use_viscol = false; char_u type = 0; @@ -210,7 +220,7 @@ qf_init_ext ( char_u *efm; char_u *ptr; char_u *srcptr; - int len; + size_t len; int i; int round; int idx = 0; @@ -221,6 +231,7 @@ qf_init_ext ( 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; @@ -243,7 +254,8 @@ qf_init_ext ( }; 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) { @@ -466,36 +478,66 @@ 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 (len > CMDBUFFSIZE - 2) - STRLCPY(IObuff, p_str, CMDBUFFSIZE - 1); + if (p != NULL) + len = (size_t)(p - p_str) + 1; else - STRLCPY(IObuff, p_str, len + 1); + len = STRLEN(p_str); + + if (len > IOSIZE - 2) { + // If the line exceeds LINE_MAXLEN exclude the last byte since it's + // not a NL character. + linelen = len > LINE_MAXLEN ? LINE_MAXLEN - 1 : len; + if (growbuf == NULL) { + growbuf = xmalloc(linelen); + } else if (linelen > growbufsiz) { + growbuf = xrealloc(growbuf, linelen); + } + growbufsiz = linelen; + linebuf = growbuf; + } 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) { + linelen = len; + if (linelen > LINE_MAXLEN) { + linelen = LINE_MAXLEN - 1; + } + if (growbuf == NULL) { + growbuf = xmalloc(linelen); + growbufsiz = linelen; + } else if (linelen > growbufsiz) { + growbuf = xrealloc(growbuf, linelen); + growbufsiz = linelen; + } + linebuf = growbuf; + } 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 +545,103 @@ 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); + linelen = STRLEN(p_buf); + if (linelen > IOSIZE - 2) { + if (growbuf == NULL) { + growbuf = xmalloc(linelen); + growbufsiz = linelen; + } else if (linelen > growbufsiz) { + if (linelen > LINE_MAXLEN) { + linelen = LINE_MAXLEN - 1; + } + growbuf = xrealloc(growbuf, linelen); + growbufsiz = linelen; + } + linebuf = growbuf; + } else { + linebuf = IObuff; + } + 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 + )) { + // 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) @@ -547,7 +671,7 @@ 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) { @@ -596,12 +720,22 @@ 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 (fmt_ptr->flags == '+' && !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,7 +769,7 @@ 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]); + len = (size_t)(regmatch.endp[i] - regmatch.startp[i]); if (len > CMDBUFFSIZE - 5) len = CMDBUFFSIZE - 5; STRCPY(pattern, "^\\V"); @@ -664,7 +798,12 @@ restofline: 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; } @@ -785,6 +924,7 @@ qf_init_end: xfree(errmsg); xfree(pattern); xfree(fmtstr); + xfree(growbuf); qf_update_buffer(qi, true); -- cgit From 48b5d0f1ba1ad75411f6510ed79d322116ba6809 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 12 Dec 2016 15:04:52 -0500 Subject: vim-patch:7.4.1813 Problem: Memory access error when running test_quickfix. Solution: Allocate one more byte. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/9b4ebc692d77ca8ef90d72517347f74c2474dd3d --- src/nvim/quickfix.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/quickfix.c') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 3c59fcef03..b4f8750c4c 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -492,9 +492,9 @@ qf_init_ext ( // not a NL character. linelen = len > LINE_MAXLEN ? LINE_MAXLEN - 1 : len; if (growbuf == NULL) { - growbuf = xmalloc(linelen); + growbuf = xmalloc(linelen + 1); } else if (linelen > growbufsiz) { - growbuf = xrealloc(growbuf, linelen); + growbuf = xrealloc(growbuf, linelen + 1); } growbufsiz = linelen; linebuf = growbuf; @@ -525,10 +525,10 @@ qf_init_ext ( linelen = LINE_MAXLEN - 1; } if (growbuf == NULL) { - growbuf = xmalloc(linelen); + growbuf = xmalloc(linelen + 1); growbufsiz = linelen; } else if (linelen > growbufsiz) { - growbuf = xrealloc(growbuf, linelen); + growbuf = xrealloc(growbuf, linelen + 1); growbufsiz = linelen; } linebuf = growbuf; @@ -549,13 +549,13 @@ qf_init_ext ( linelen = STRLEN(p_buf); if (linelen > IOSIZE - 2) { if (growbuf == NULL) { - growbuf = xmalloc(linelen); + growbuf = xmalloc(linelen + 1); growbufsiz = linelen; } else if (linelen > growbufsiz) { if (linelen > LINE_MAXLEN) { linelen = LINE_MAXLEN - 1; } - growbuf = xrealloc(growbuf, linelen); + growbuf = xrealloc(growbuf, linelen + 1); growbufsiz = linelen; } linebuf = growbuf; -- cgit From b9c5ca21e6a666dfcd3324720ca59ed52ce34883 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 12 Dec 2016 15:04:57 -0500 Subject: vim-patch:7.4.1815 Problem: Compiler warnings for unused variables. (Ajit Thakkar) Solution: Add a dummy initialization. (Yasuhiro Matsumoto) https://github.com/vim/vim/commit/9a3b3311d26c990208150255ad65472bb4eefaa4 --- src/nvim/quickfix.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/quickfix.c') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index b4f8750c4c..1188ed7fd4 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -200,9 +200,9 @@ qf_init_ext ( char_u *fmtstr = NULL; char_u *growbuf = NULL; size_t growbuflen; - size_t growbufsiz; - char_u *linebuf; - size_t linelen; + size_t growbufsiz = 0; + char_u *linebuf = NULL; + size_t linelen = 0; bool discard; int col = 0; bool use_viscol = false; -- cgit From 9df9cf4ecc4143ce23b5ab85b2ad1ef413a249bd Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 12 Dec 2016 15:05:01 -0500 Subject: vim-patch:7.4.1841 Problem: The code to reallocate the buffer used for quickfix is repeated. Solution: Move the code to a function. (Yegappan Lakshmanan, closes vim/vim#831) https://github.com/vim/vim/commit/2b2b8ae5ab37b04584633c469265d85825166905 --- src/nvim/quickfix.c | 52 +++++++++++++++++++--------------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) (limited to 'src/nvim/quickfix.c') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 1188ed7fd4..a68c4eca2e 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -171,6 +171,22 @@ qf_init ( // 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; +} + /* * Read the errorfile "efile" into memory, line by line, building the error * list. @@ -488,16 +504,7 @@ qf_init_ext ( len = STRLEN(p_str); if (len > IOSIZE - 2) { - // If the line exceeds LINE_MAXLEN exclude the last byte since it's - // not a NL character. - linelen = len > LINE_MAXLEN ? LINE_MAXLEN - 1 : len; - if (growbuf == NULL) { - growbuf = xmalloc(linelen + 1); - } else if (linelen > growbufsiz) { - growbuf = xrealloc(growbuf, linelen + 1); - } - growbufsiz = linelen; - linebuf = growbuf; + linebuf = qf_grow_linebuf(&growbuf, &growbufsiz, len, &linelen); } else { linebuf = IObuff; linelen = len; @@ -520,18 +527,7 @@ qf_init_ext ( len = STRLEN(p_li->li_tv.vval.v_string); if (len > IOSIZE - 2) { - linelen = len; - if (linelen > LINE_MAXLEN) { - linelen = LINE_MAXLEN - 1; - } - if (growbuf == NULL) { - growbuf = xmalloc(linelen + 1); - growbufsiz = linelen; - } else if (linelen > growbufsiz) { - growbuf = xrealloc(growbuf, linelen + 1); - growbufsiz = linelen; - } - linebuf = growbuf; + linebuf = qf_grow_linebuf(&growbuf, &growbufsiz, len, &linelen); } else { linebuf = IObuff; linelen = len; @@ -548,17 +544,7 @@ qf_init_ext ( p_buf = ml_get_buf(buf, buflnum++, false); linelen = STRLEN(p_buf); if (linelen > IOSIZE - 2) { - if (growbuf == NULL) { - growbuf = xmalloc(linelen + 1); - growbufsiz = linelen; - } else if (linelen > growbufsiz) { - if (linelen > LINE_MAXLEN) { - linelen = LINE_MAXLEN - 1; - } - growbuf = xrealloc(growbuf, linelen + 1); - growbufsiz = linelen; - } - linebuf = growbuf; + linebuf = qf_grow_linebuf(&growbuf, &growbufsiz, len, &linelen); } else { linebuf = IObuff; } -- cgit From d244068f4fda821888a08858c8a00722f0326da6 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 12 Dec 2016 15:05:06 -0500 Subject: vim-patch:7.4.1871 Problem: Appending to the quickfix list while the quickfix window is open is very slow. Solution: Do not delete all the lines, only append the new ones. Avoid using a window while updating the list. (closes vim/vim#841) https://github.com/vim/vim/commit/864293abb72d62604d8d6b458addfb43c14230c3 --- src/nvim/quickfix.c | 144 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 55 deletions(-) (limited to 'src/nvim/quickfix.c') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index a68c4eca2e..dcc4ebcc1b 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -187,15 +187,14 @@ static char_u *qf_grow_linebuf(char_u **growbuf, size_t *growbufsiz, return *growbuf; } -/* - * 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. - */ +// 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, @@ -228,6 +227,7 @@ qf_init_ext ( int enr = 0; FILE *fd = NULL; qfline_T *qfprev = NULL; /* init to make SASC shut up */ + qfline_T *old_last = NULL; char_u *efmp; efm_T *fmt_first = NULL; efm_T *fmt_last = NULL; @@ -282,11 +282,13 @@ qf_init_ext ( 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) + 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) ; + old_last = qfprev; + } /* * Each part of the format string is copied and modified from errorformat to @@ -912,7 +914,7 @@ qf_init_end: xfree(fmtstr); xfree(growbuf); - qf_update_buffer(qi, true); + qf_update_buffer(qi, old_last); return retval; } @@ -2053,7 +2055,7 @@ 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, true); + qf_update_buffer(qi, NULL); } /* @@ -2321,7 +2323,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; @@ -2440,7 +2442,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, bool update_cursor) +static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last) { buf_T *buf; win_T *win; @@ -2450,8 +2452,12 @@ static void qf_update_buffer(qf_info_T *qi, bool update_cursor) /* 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; @@ -2459,12 +2465,20 @@ static void qf_update_buffer(qf_info_T *qi, bool update_cursor) qf_set_title_var(qi); curwin = curwin_save; } - qf_fill_buffer(qi); - /* restore curwin/curbuf and a few other things */ - aucmd_restbuf(&aco); - if (update_cursor) { + qf_fill_buffer(qi, buf, old_last); + + 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); } } } @@ -2478,11 +2492,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; @@ -2490,15 +2505,28 @@ 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) { + 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) { @@ -2538,33 +2566,38 @@ 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) + if (ml_append_buf(buf, lnum, IObuff, (colnr_T)STRLEN(IObuff) + 1, FALSE) == FAIL) break; + lnum++; qfp = qfp->qf_next; } - /* Delete the empty line which is now at the end */ - (void)ml_delete(lnum + 1, FALSE); + if (old_last == NULL) { + /* 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; + 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; + 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); + /* make sure it will be redrawn */ + redraw_curbuf_later(NOT_VALID); + } /* Restore KeyTyped, setting 'filetype' may reset it. */ KeyTyped = old_KeyTyped; @@ -3367,7 +3400,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, true); + qf_update_buffer(qi, NULL); if (au_name != NULL) apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, @@ -3655,15 +3688,15 @@ int get_errorlist(win_T *wp, list_T *list) 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; @@ -3675,12 +3708,13 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) 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) + 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') { + old_last = prevp; + } else if (action == 'r') { qf_free(qi, qi->qf_curlist); qf_store_title(qi, title); } @@ -3759,7 +3793,7 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) } // Don't update the cursor in quickfix window when appending entries - qf_update_buffer(qi, (action != 'a')); + qf_update_buffer(qi, old_last); return retval; } @@ -4015,7 +4049,7 @@ void ex_helpgrep(exarg_T *eap) /* Darn, some plugin changed the value. */ free_string_option(save_cpo); - qf_update_buffer(qi, true); + qf_update_buffer(qi, NULL); if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, -- cgit From 1e49cf6f233f6681a5a269bba84a09d8f8dbac22 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 12 Dec 2016 15:05:10 -0500 Subject: vim-patch:7.4.1881 Problem: Appending to a long quickfix list is slow. Solution: Add qf_last. https://github.com/vim/vim/commit/83e6d7ac6a1c2a0cb5ee6c8420a5dc792f1d5ffa --- src/nvim/quickfix.c | 94 ++++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) (limited to 'src/nvim/quickfix.c') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index dcc4ebcc1b..34918e4134 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -77,6 +77,7 @@ struct qfline_S { typedef struct qf_list_S { 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 */ @@ -226,7 +227,6 @@ qf_init_ext ( long lnum = 0L; int enr = 0; FILE *fd = NULL; - qfline_T *qfprev = NULL; /* init to make SASC shut up */ qfline_T *old_last = NULL; char_u *efmp; efm_T *fmt_first = NULL; @@ -283,11 +283,8 @@ qf_init_ext ( /* 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) - ; - old_last = qfprev; + /* Adding to existing list, use last entry. */ + old_last = qi->qf_lists[qi->qf_curlist].qf_last; } /* @@ -805,6 +802,7 @@ restofline: multiignore = false; // reset continuation } else if (vim_strchr((char_u *)"CZ", idx) != 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) { @@ -857,7 +855,7 @@ restofline: } } - if (qf_add_entry(qi, &qfprev, + if (qf_add_entry(qi, directory, (*namebuf || directory) ? namebuf @@ -997,7 +995,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 @@ -1011,12 +1008,12 @@ 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) qfp->qf_fnum = bufnum; @@ -1037,20 +1034,21 @@ 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; + assert(*lastp); + qfp->qf_prev = *lastp; + (*lastp)->qf_next = qfp; } - qfp->qf_next = qfp; /* last element points to itself */ + qfp->qf_next = NULL; qfp->qf_cleared = FALSE; - *prevp = qfp; + *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 */ @@ -1136,6 +1134,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); @@ -1144,12 +1143,13 @@ void copy_loclist(win_T *from, win_T *to) if (from_qfl->qf_count) { qfline_T *from_qfp; - qfline_T *prevp = NULL; + 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; + 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, &prevp, + if (qf_add_entry(to->w_llist, NULL, NULL, 0, @@ -1169,6 +1169,7 @@ 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 = 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) @@ -1400,7 +1401,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; } } @@ -1981,6 +1982,9 @@ void qf_list(exarg_T *eap) } qfp = qfp->qf_next; + if (qfp == NULL) { + break; + } ++i; os_breakcheck(); } @@ -2064,22 +2068,24 @@ static void qf_msg(qf_info_T *qi) static void qf_free(qf_info_T *qi, int idx) { qfline_T *qfp; + qfline_T *qfpnext; int 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); + 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: Avoid qf_count being incorrect. */ qi->qf_lists[idx].qf_count = 1; } - qi->qf_lists[idx].qf_start = qfp; + qi->qf_lists[idx].qf_start = qfpnext; --qi->qf_lists[idx].qf_count; } xfree(qi->qf_lists[idx].qf_title); @@ -2108,7 +2114,8 @@ 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) { if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2) { if (amount == MAXLNUM) @@ -2571,6 +2578,9 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last) break; lnum++; qfp = qfp->qf_next; + if (qfp == NULL) { + break; + } } if (old_last == NULL) { /* Delete the empty line which is now at the end */ @@ -3123,7 +3133,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; @@ -3208,12 +3217,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 */ @@ -3311,7 +3314,7 @@ void ex_vimgrep(exarg_T *eap) while (vim_regexec_multi(®match, curwin, buf, lnum, col, NULL) > 0) { ; - if (qf_add_entry(qi, &prevp, + if (qf_add_entry(qi, NULL, /* dir */ fname, 0, @@ -3684,6 +3687,9 @@ int get_errorlist(win_T *wp, list_T *list) return FAIL; qfp = qfp->qf_next; + if (qfp == NULL) { + break; + } } return OK; } @@ -3695,7 +3701,6 @@ 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; @@ -3709,11 +3714,8 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) /* 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) - ; - old_last = prevp; + // 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); @@ -3756,7 +3758,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, @@ -3898,7 +3899,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; @@ -4001,7 +4001,7 @@ void ex_helpgrep(exarg_T *eap) while (l > 0 && line[l - 1] <= ' ') line[--l] = NUL; - if (qf_add_entry(qi, &prevp, + if (qf_add_entry(qi, NULL, /* dir */ fnames[fi], 0, -- cgit From a86d8b4088d21b784c1b72a03db9c22dcae67d00 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 12 Dec 2016 15:05:15 -0500 Subject: vim-patch:7.4.1882 Problem: Check for line break at end of line wrong. (Dominique Pelle) Solution: Correct the logic. https://github.com/vim/vim/commit/b37662a0fbb952838fca87aff4d26b596030b67b --- src/nvim/quickfix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/quickfix.c') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 34918e4134..9703d8763a 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -556,9 +556,9 @@ qf_init_ext ( discard = false; linelen = STRLEN(IObuff); - if (linelen == IOSIZE - 1 && (IObuff[linelen - 1] != '\n' + if (linelen == IOSIZE - 1 && !(IObuff[linelen - 1] == '\n' #ifdef USE_CRNL - || IObuff[linelen - 1] != '\r' + || IObuff[linelen - 1] == '\r' #endif )) { // The current line exceeds IObuff, continue reading using growbuf -- cgit From ab43303df7f09d1789a70e2536552461657f08ac Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 12 Dec 2016 15:05:20 -0500 Subject: vim-patch:7.4.1884 Problem: Updating marks in a quickfix list is very slow when the list is long. Solution: Only update marks if the buffer has a quickfix entry. https://github.com/vim/vim/commit/2f095a4bc4d786e0ac834f48dd18a94fe2d140e3 --- src/nvim/quickfix.c | 78 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 31 deletions(-) (limited to 'src/nvim/quickfix.c') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 9703d8763a..9b2dbf1bf5 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1015,9 +1015,14 @@ static int qf_add_entry(qf_info_T *qi, char_u *dir, char_u *fname, int bufnum, qfline_T *qfp = xmalloc(sizeof(qfline_T)); qfline_T **lastp; // pointer to qf_last or NULL - if (bufnum != 0) + if (bufnum != 0) { + buf_T *buf = buflist_findnr(bufnum); + qfp->qf_fnum = bufnum; - else + if (buf != NULL) { + buf->b_has_qf_entry = true; + } + } else qfp->qf_fnum = qf_get_fnum(dir, fname); qfp->qf_text = vim_strsave(mesg); qfp->qf_lnum = lnum; @@ -1190,45 +1195,47 @@ 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" - */ +// Get buffer number for file "dir.name". +// Also sets the b_has_qf_entry flag. static int qf_get_fnum(char_u *directory, char_u *fname) { + 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(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; } /* @@ -2104,7 +2111,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; @@ -2117,6 +2128,7 @@ void qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long 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; @@ -2125,6 +2137,10 @@ 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; + } } /* -- cgit From 63d4ac7b2515072bffabe1d69fd6eb62aeb860f4 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 12 Dec 2016 15:05:38 -0500 Subject: vim-patch:7.4.1949 Problem: Minor problems with the quickfix code. Solution: Fix the problems. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/38df43bd13a2498cc96b3ddd9a20dd75126bd171 --- src/nvim/quickfix.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/nvim/quickfix.c') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 9b2dbf1bf5..16d336c41f 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -541,11 +541,12 @@ qf_init_ext ( if (buflnum > lnumlast) break; p_buf = ml_get_buf(buf, buflnum++, false); - linelen = STRLEN(p_buf); - if (linelen > IOSIZE - 2) { + 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); } @@ -1238,10 +1239,8 @@ static int qf_get_fnum(char_u *directory, char_u *fname) return buf->b_fnum; } -/* - * push dirbuf onto the directory stack and return pointer to actual dir or - * NULL on error - */ +// 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) { struct dir_stack_T *ds_ptr; -- cgit From 82f54c0a5eea91dcfd7a509588b4ac37f7e55b57 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Mon, 12 Dec 2016 15:05:52 -0500 Subject: vim-patch:7.4.1964 Problem: The quickfix init function is too big. Solution: Factor out parsing 'errorformat' to a separate function. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/688e3d1fd9b9129a5ba0e0d599ccfe6f4443daf3 --- src/nvim/quickfix.c | 430 +++++++++++++++++++++++++++++----------------------- 1 file changed, 239 insertions(+), 191 deletions(-) (limited to 'src/nvim/quickfix.c') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 16d336c41f..1499c3e12f 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -188,6 +188,243 @@ static char_u *qf_grow_linebuf(char_u **growbuf, size_t *growbufsiz, 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', "[- .]*"}, + {'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]) { + sprintf((char *)errmsg, + _("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)) { + sprintf((char *)errmsg, + _("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: scanf()-like: %*ud, %*3c, %*f, ... ? + sprintf((char *)errmsg, + _("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 { + sprintf((char *)errmsg, + _("E376: Invalid %%%c in format string prefix"), *efmp); + EMSG(errmsg); + return -1; + } + } else { + sprintf((char *)errmsg, + _("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); + } +#ifdef COLON_IN_FILENAME + i += 12; /* "%f" can become twelve chars longer */ +#else + i += 2; /* "%f" can become two chars longer */ +#endif + 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". @@ -213,7 +450,6 @@ qf_init_ext ( char_u *errmsg; size_t errmsglen; char_u *pattern; - char_u *fmtstr = NULL; char_u *growbuf = NULL; size_t growbuflen; size_t growbufsiz = 0; @@ -228,17 +464,13 @@ qf_init_ext ( int enr = 0; FILE *fd = NULL; qfline_T *old_last = NULL; - char_u *efmp; efm_T *fmt_first = NULL; - efm_T *fmt_last = NULL; efm_T *fmt_ptr; efm_T *fmt_start = NULL; char_u *efm; char_u *ptr; - char_u *srcptr; size_t len; int i; - int round; int idx = 0; bool multiline = false; bool multiignore = false; @@ -252,22 +484,6 @@ qf_init_ext ( 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); errmsglen = CMDBUFFSIZE + 1; @@ -296,172 +512,9 @@ 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; - - /* - * 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;