From 1cfe83c2a2d5d1d5dcc37bdcdb9dba4107e41de7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 5 Mar 2023 14:47:51 +0800 Subject: [PATCH 1/4] vim-patch:9.0.0736: quickfix listing does not handle very long messages Problem: Quickfix listing does not handle very long messages. Solution: Use a growarray instead of a fixed size buffer. (Yegappan Lakshmanan, closes vim/vim#11357) https://github.com/vim/vim/commit/f8412c9d7cc487dacf47a217ae947da68a525c53 Override Test_very_long_error_line() with a rewrite that doesn't use deferred delete and string interpolation. Co-authored-by: Yegappan Lakshmanan --- src/nvim/quickfix.c | 147 ++++++++++++++--------------- src/nvim/testdir/test_quickfix.vim | 60 ++++++++++++ 2 files changed, 130 insertions(+), 77 deletions(-) diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 5518fdfa51..af2d10798a 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -2807,13 +2807,15 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf update_screen(); } } - snprintf(IObuff, IOSIZE, _("(%d of %d)%s%s: "), qf_index, - qf_get_curlist(qi)->qf_count, - qf_ptr->qf_cleared ? _(" (line deleted)") : "", - qf_types(qf_ptr->qf_type, qf_ptr->qf_nr)); + vim_snprintf(IObuff, IOSIZE, _("(%d of %d)%s%s: "), qf_index, + qf_get_curlist(qi)->qf_count, + qf_ptr->qf_cleared ? _(" (line deleted)") : "", + qf_types(qf_ptr->qf_type, qf_ptr->qf_nr)); // Add the message, skipping leading whitespace and newlines. - int len = (int)strlen(IObuff); - qf_fmt_text(skipwhite(qf_ptr->qf_text), IObuff + len, IOSIZE - len); + garray_T ga; + ga_init(&ga, 1, 256); + ga_concat(&ga, IObuff); + qf_fmt_text(&ga, skipwhite(qf_ptr->qf_text)); // Output the message. Overwrite to avoid scrolling when the 'O' // flag is present in 'shortmess'; But when not jumping, print the @@ -2825,8 +2827,9 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf msg_scroll = false; } msg_ext_set_kind("quickfix"); - msg_attr_keep(IObuff, 0, true, false); + msg_attr_keep(ga.ga_data, 0, true, false); msg_scroll = (int)i; + ga_clear(&ga); } /// Find a usable window for opening a file from the quickfix/location list. If @@ -3086,41 +3089,32 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) if (qfp->qf_lnum != 0) { msg_puts_attr(":", qfSepAttr); } + garray_T ga; + ga_init(&ga, 1, 256); if (qfp->qf_lnum == 0) { - IObuff[0] = NUL; + ga_append(&ga, NUL); } else { - qf_range_text(qfp, IObuff, IOSIZE); + qf_range_text(&ga, qfp); } - vim_snprintf(IObuff + strlen(IObuff), IOSIZE, "%s", qf_types(qfp->qf_type, qfp->qf_nr)); - msg_puts_attr((const char *)IObuff, qfLineAttr); + ga_concat(&ga, qf_types(qfp->qf_type, qfp->qf_nr)); + ga_append(&ga, NUL); + msg_puts_attr(ga.ga_data, qfLineAttr); + ga_clear(&ga); msg_puts_attr(":", qfSepAttr); if (qfp->qf_pattern != NULL) { - qf_fmt_text(qfp->qf_pattern, IObuff, IOSIZE); - msg_puts((const char *)IObuff); + qf_fmt_text(&ga, qfp->qf_pattern); + msg_puts(ga.ga_data); + ga_clear(&ga); msg_puts_attr(":", qfSepAttr); } msg_puts(" "); - char *tbuf = IObuff; - size_t tbuflen = IOSIZE; - size_t len = strlen(qfp->qf_text) + 3; - - if (len > IOSIZE) { - tbuf = xmalloc(len); - tbuflen = len; - } - // Remove newlines and leading whitespace from the text. For an // unrecognized line keep the indent, the compiler may mark a word // with ^^^^. - qf_fmt_text((fname != NULL || qfp->qf_lnum != 0) - ? skipwhite(qfp->qf_text) : qfp->qf_text, - tbuf, (int)tbuflen); - msg_prt_line(tbuf, false); - - if (tbuf != IObuff) { - xfree(tbuf); - } + qf_fmt_text(&ga, (fname != NULL || qfp->qf_lnum != 0) ? skipwhite(qfp->qf_text) : qfp->qf_text); + msg_prt_line(ga.ga_data, false); + ga_clear(&ga); } // ":clist": list all errors @@ -3197,49 +3191,54 @@ void qf_list(exarg_T *eap) } } -// Remove newlines and leading whitespace from an error message. -// Put the result in "buf[bufsize]". -static void qf_fmt_text(const char *restrict text, char *restrict buf, int bufsize) +/// Remove newlines and leading whitespace from an error message. +/// Add the result to the grow array "gap". +static void qf_fmt_text(garray_T *gap, const char *restrict text) FUNC_ATTR_NONNULL_ALL { - int i; const char *p = (char *)text; - for (i = 0; *p != NUL && i < bufsize - 1; i++) { + while (*p != NUL) { if (*p == '\n') { - buf[i] = ' '; + ga_append(gap, ' '); while (*++p != NUL) { if (!ascii_iswhite(*p) && *p != '\n') { break; } } } else { - buf[i] = *p++; + ga_append(gap, (uint8_t)(*p++)); } } - buf[i] = NUL; + + ga_append(gap, NUL); } -// Range information from lnum, col, end_lnum, and end_col. -// Put the result in "buf[bufsize]". -static void qf_range_text(const qfline_T *qfp, char *buf, int bufsize) +/// Add the range information from the lnum, col, end_lnum, and end_col values +/// of a quickfix entry to the grow array "gap". +static void qf_range_text(garray_T *gap, const qfline_T *qfp) { - vim_snprintf(buf, (size_t)bufsize, "%" PRIdLINENR, qfp->qf_lnum); - int len = (int)strlen(buf); + char *const buf = IObuff; + const size_t bufsize = IOSIZE; + + vim_snprintf(buf, bufsize, "%" PRIdLINENR, qfp->qf_lnum); + size_t len = strlen(buf); if (qfp->qf_end_lnum > 0 && qfp->qf_lnum != qfp->qf_end_lnum) { - vim_snprintf(buf + len, (size_t)(bufsize - len), "-%" PRIdLINENR, qfp->qf_end_lnum); - len += (int)strlen(buf + len); + vim_snprintf(buf + len, bufsize - len, "-%" PRIdLINENR, qfp->qf_end_lnum); + len += strlen(buf + len); } if (qfp->qf_col > 0) { - vim_snprintf(buf + len, (size_t)(bufsize - len), " col %d", qfp->qf_col); - len += (int)strlen(buf + len); + vim_snprintf(buf + len, bufsize - len, " col %d", qfp->qf_col); + len += strlen(buf + len); if (qfp->qf_end_col > 0 && qfp->qf_col != qfp->qf_end_col) { - vim_snprintf(buf + len, (size_t)(bufsize - len), "-%d", qfp->qf_end_col); - len += (int)strlen(buf + len); + vim_snprintf(buf + len, bufsize - len, "-%d", qfp->qf_end_col); + len += strlen(buf + len); } } buf[len] = NUL; + + ga_concat_len(gap, buf, len); } /// Display information (list number, list size and the title) about a @@ -3945,21 +3944,22 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli char *dirname, char *qftf_str, bool first_bufline) FUNC_ATTR_NONNULL_ARG(1, 2, 4, 5) { + garray_T ga; + ga_init(&ga, 1, 256); + // If the 'quickfixtextfunc' function returned a non-empty custom string // for this entry, then use it. if (qftf_str != NULL && *qftf_str != NUL) { - xstrlcpy(IObuff, qftf_str, IOSIZE); + ga_concat(&ga, qftf_str); } else { buf_T *errbuf; - int len; if (qfp->qf_module != NULL) { - xstrlcpy(IObuff, qfp->qf_module, IOSIZE); - len = (int)strlen(IObuff); + ga_concat(&ga, qfp->qf_module); } else if (qfp->qf_fnum != 0 && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL && errbuf->b_fname != NULL) { if (qfp->qf_type == 1) { // :helpgrep - xstrlcpy(IObuff, path_tail(errbuf->b_fname), IOSIZE); + ga_concat(&ga, path_tail(errbuf->b_fname)); } else { // Shorten the file name if not done already. // For optimization, do this only for the first entry in a @@ -3972,42 +3972,35 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli } shorten_buf_fname(errbuf, dirname, false); } - xstrlcpy(IObuff, errbuf->b_fname, IOSIZE); + ga_concat(&ga, errbuf->b_fname); } - len = (int)strlen(IObuff); - } else { - len = 0; } - if (len < IOSIZE - 1) { - IObuff[len++] = '|'; - } - if (qfp->qf_lnum > 0) { - qf_range_text(qfp, IObuff + len, IOSIZE - len); - len += (int)strlen(IObuff + len); - snprintf(IObuff + len, (size_t)(IOSIZE - len), "%s", qf_types(qfp->qf_type, - qfp->qf_nr)); - len += (int)strlen(IObuff + len); + ga_append(&ga, '|'); + + if (qfp->qf_lnum > 0) { + qf_range_text(&ga, qfp); + ga_concat(&ga, qf_types(qfp->qf_type, qfp->qf_nr)); } else if (qfp->qf_pattern != NULL) { - qf_fmt_text(qfp->qf_pattern, IObuff + len, IOSIZE - len); - len += (int)strlen(IObuff + len); - } - if (len < IOSIZE - 2) { - IObuff[len++] = '|'; - IObuff[len++] = ' '; + qf_fmt_text(&ga, qfp->qf_pattern); } + ga_append(&ga, '|'); + ga_append(&ga, ' '); // Remove newlines and leading whitespace from the text. // For an unrecognized line keep the indent, the compiler may // mark a word with ^^^^. - qf_fmt_text(len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text, - IObuff + len, IOSIZE - len); + qf_fmt_text(&ga, ga.ga_len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text); } - if (ml_append_buf(buf, lnum, IObuff, - (colnr_T)strlen(IObuff) + 1, false) == FAIL) { + ga_append(&ga, NUL); + + if (ml_append_buf(buf, lnum, ga.ga_data, ga.ga_len + 1, false) == FAIL) { return FAIL; } + + ga_clear(&ga); + return OK; } diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 8dc4173d60..fedc486e62 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -6220,6 +6220,66 @@ func Test_loclist_replace_autocmd() call setloclist(0, [], 'f') endfunc +" Test for a very long error line and a very long information line +func Test_very_long_error_line() + let msg = repeat('abcdefghijklmn', 146) + let emsg = 'Xlonglines.c:1:' . msg + call writefile([msg, emsg], 'Xerror', 'D') + cfile Xerror + cwindow + call assert_equal($'|| {msg}', getline(1)) + call assert_equal($'Xlonglines.c|1| {msg}', getline(2)) + cclose + + let l = execute('clist!')->split("\n") + call assert_equal([$' 1: {msg}', $' 2 Xlonglines.c:1: {msg}'], l) + + let l = execute('cc')->split("\n") + call assert_equal([$'(2 of 2): {msg}'], l) + + call setqflist([], 'f') +endfunc + +" The test depends on deferred delete and string interpolation, which haven't +" been ported, so override it with a rewrite that doesn't use these features. +func! Test_very_long_error_line() + let msg = repeat('abcdefghijklmn', 146) + let emsg = 'Xlonglines.c:1:' . msg + call writefile([msg, emsg], 'Xerror') + cfile Xerror + call delete('Xerror') + cwindow + call assert_equal('|| ' .. msg, getline(1)) + call assert_equal('Xlonglines.c|1| ' .. msg, getline(2)) + cclose + + let l = execute('clist!')->split("\n") + call assert_equal([' 1: ' .. msg, ' 2 Xlonglines.c:1: ' .. msg], l) + + let l = execute('cc')->split("\n") + call assert_equal(['(2 of 2): ' .. msg], l) + + call setqflist([], 'f') +endfunc + +" In the quickfix window, spaces at the beginning of an informational line +" should not be removed but should be removed from an error line. +func Test_info_line_with_space() + cexpr ["a.c:20:12: error: expected ';' before ':' token", + \ ' 20 | Afunc():', '', ' | ^'] + copen + call assert_equal(["a.c|20 col 12| error: expected ';' before ':' token", + \ '|| 20 | Afunc():', '|| ', + \ '|| | ^'], getline(1, '$')) + cclose + + let l = execute('clist!')->split("\n") + call assert_equal([" 1 a.c:20 col 12: error: expected ';' before ':' token", + \ ' 2: 20 | Afunc():', ' 3: ', ' 4: | ^'], l) + + call setqflist([], 'f') +endfunc + func s:QfTf(_) endfunc From 1adad44b7ce6574f505f4cf5df3c8e21c0747f93 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 5 Mar 2023 16:52:47 +0800 Subject: [PATCH 2/4] vim-patch:9.0.0749: alloc/free of buffer for each quickfix entry is inefficient Problem: Alloc/free of buffer for each quickfix entry is inefficient. Solution: Use a shared grow array. (Yegappan Lakshmanan, closes vim/vim#11365) https://github.com/vim/vim/commit/975a665d4811649a51e2c6a97a6ce096290d87ae Co-authored-by: Yegappan Lakshmanan --- src/nvim/memory.c | 6 +-- src/nvim/quickfix.c | 107 +++++++++++++++++++++++++++----------------- 2 files changed, 67 insertions(+), 46 deletions(-) diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 4e799dfd08..ffeafbdf2c 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -755,11 +755,7 @@ void free_all_mem(void) p_hi = 0; init_history(); - qf_free_all(NULL); - // Free all location lists - FOR_ALL_TAB_WINDOWS(tab, win) { - qf_free_all(win); - } + free_quickfix(); // Close all script inputs. close_all_scripts(); diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index af2d10798a..51cc7fa30b 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -245,6 +245,10 @@ typedef struct vgr_args_S { #endif static char *e_no_more_items = N_("E553: No more items"); +static char *e_current_quickfix_list_was_changed = + N_("E925: Current quickfix list was changed"); +static char *e_current_location_list_was_changed = + N_("E926: Current location list was changed"); // Quickfix window check helper macro #define IS_QF_WINDOW(wp) (bt_quickfix((wp)->w_buffer) && (wp)->w_llist_ref == NULL) @@ -275,10 +279,24 @@ static char *e_no_more_items = N_("E553: No more items"); static char *qf_last_bufname = NULL; static bufref_T qf_last_bufref = { NULL, 0, 0 }; -static char *e_current_quickfix_list_was_changed = - N_("E925: Current quickfix list was changed"); -static char *e_current_location_list_was_changed = - N_("E926: Current location list was changed"); +static garray_T qfga; + +/// Get a growarray to buffer text in. Shared between various commands to avoid +/// many alloc/free calls. +static garray_T *qfga_get(void) +{ + static bool initialized = false; + + if (!initialized) { + initialized = true; + ga_init(&qfga, 1, 256); + } + + // Retain ga_data from previous use. Reset the length to zero. + qfga.ga_len = 0; + + return &qfga; +} // Counter to prevent autocmds from freeing up location lists when they are // still being used. @@ -2799,6 +2817,8 @@ static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char qf_viscol, char static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf_T *old_curbuf, linenr_T old_lnum) { + garray_T *const gap = qfga_get(); + // Update the screen before showing the message, unless the screen // scrolled up. if (!msg_scrolled) { @@ -2812,10 +2832,8 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf qf_ptr->qf_cleared ? _(" (line deleted)") : "", qf_types(qf_ptr->qf_type, qf_ptr->qf_nr)); // Add the message, skipping leading whitespace and newlines. - garray_T ga; - ga_init(&ga, 1, 256); - ga_concat(&ga, IObuff); - qf_fmt_text(&ga, skipwhite(qf_ptr->qf_text)); + ga_concat(gap, IObuff); + qf_fmt_text(gap, skipwhite(qf_ptr->qf_text)); // Output the message. Overwrite to avoid scrolling when the 'O' // flag is present in 'shortmess'; But when not jumping, print the @@ -2827,9 +2845,8 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf msg_scroll = false; } msg_ext_set_kind("quickfix"); - msg_attr_keep(ga.ga_data, 0, true, false); + msg_attr_keep(gap->ga_data, 0, true, false); msg_scroll = (int)i; - ga_clear(&ga); } /// Find a usable window for opening a file from the quickfix/location list. If @@ -3089,22 +3106,20 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) if (qfp->qf_lnum != 0) { msg_puts_attr(":", qfSepAttr); } - garray_T ga; - ga_init(&ga, 1, 256); + garray_T *gap = qfga_get(); if (qfp->qf_lnum == 0) { - ga_append(&ga, NUL); + ga_append(gap, NUL); } else { - qf_range_text(&ga, qfp); + qf_range_text(gap, qfp); } - ga_concat(&ga, qf_types(qfp->qf_type, qfp->qf_nr)); - ga_append(&ga, NUL); - msg_puts_attr(ga.ga_data, qfLineAttr); - ga_clear(&ga); + ga_concat(gap, qf_types(qfp->qf_type, qfp->qf_nr)); + ga_append(gap, NUL); + msg_puts_attr(gap->ga_data, qfLineAttr); msg_puts_attr(":", qfSepAttr); if (qfp->qf_pattern != NULL) { - qf_fmt_text(&ga, qfp->qf_pattern); - msg_puts(ga.ga_data); - ga_clear(&ga); + gap = qfga_get(); + qf_fmt_text(gap, qfp->qf_pattern); + msg_puts(gap->ga_data); msg_puts_attr(":", qfSepAttr); } msg_puts(" "); @@ -3112,9 +3127,9 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) // Remove newlines and leading whitespace from the text. For an // unrecognized line keep the indent, the compiler may mark a word // with ^^^^. - qf_fmt_text(&ga, (fname != NULL || qfp->qf_lnum != 0) ? skipwhite(qfp->qf_text) : qfp->qf_text); - msg_prt_line(ga.ga_data, false); - ga_clear(&ga); + gap = qfga_get(); + qf_fmt_text(gap, (fname != NULL || qfp->qf_lnum != 0) ? skipwhite(qfp->qf_text) : qfp->qf_text); + msg_prt_line(gap->ga_data, false); } // ":clist": list all errors @@ -3944,22 +3959,21 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli char *dirname, char *qftf_str, bool first_bufline) FUNC_ATTR_NONNULL_ARG(1, 2, 4, 5) { - garray_T ga; - ga_init(&ga, 1, 256); + garray_T *gap = qfga_get(); // If the 'quickfixtextfunc' function returned a non-empty custom string // for this entry, then use it. if (qftf_str != NULL && *qftf_str != NUL) { - ga_concat(&ga, qftf_str); + ga_concat(gap, qftf_str); } else { buf_T *errbuf; if (qfp->qf_module != NULL) { - ga_concat(&ga, qfp->qf_module); + ga_concat(gap, qfp->qf_module); } else if (qfp->qf_fnum != 0 && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL && errbuf->b_fname != NULL) { if (qfp->qf_type == 1) { // :helpgrep - ga_concat(&ga, path_tail(errbuf->b_fname)); + ga_concat(gap, path_tail(errbuf->b_fname)); } else { // Shorten the file name if not done already. // For optimization, do this only for the first entry in a @@ -3972,35 +3986,33 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli } shorten_buf_fname(errbuf, dirname, false); } - ga_concat(&ga, errbuf->b_fname); + ga_concat(gap, errbuf->b_fname); } } - ga_append(&ga, '|'); + ga_append(gap, '|'); if (qfp->qf_lnum > 0) { - qf_range_text(&ga, qfp); - ga_concat(&ga, qf_types(qfp->qf_type, qfp->qf_nr)); + qf_range_text(gap, qfp); + ga_concat(gap, qf_types(qfp->qf_type, qfp->qf_nr)); } else if (qfp->qf_pattern != NULL) { - qf_fmt_text(&ga, qfp->qf_pattern); + qf_fmt_text(gap, qfp->qf_pattern); } - ga_append(&ga, '|'); - ga_append(&ga, ' '); + ga_append(gap, '|'); + ga_append(gap, ' '); // Remove newlines and leading whitespace from the text. // For an unrecognized line keep the indent, the compiler may // mark a word with ^^^^. - qf_fmt_text(&ga, ga.ga_len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text); + qf_fmt_text(gap, gap->ga_len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text); } - ga_append(&ga, NUL); + ga_append(gap, NUL); - if (ml_append_buf(buf, lnum, ga.ga_data, ga.ga_len + 1, false) == FAIL) { + if (ml_append_buf(buf, lnum, gap->ga_data, gap->ga_len + 1, false) == FAIL) { return FAIL; } - ga_clear(&ga); - return OK; } @@ -7207,6 +7219,19 @@ void ex_helpgrep(exarg_T *eap) } } +#if defined(EXITFREE) +void free_quickfix(void) +{ + qf_free_all(NULL); + // Free all location lists + FOR_ALL_TAB_WINDOWS(tab, win) { + qf_free_all(win); + } + + ga_clear(&qfga); +} +#endif + static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) { if (what_arg->v_type == VAR_UNKNOWN) { From 749fe2c383f662bb13f97336329a0e08200c0a3b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 5 Mar 2023 17:03:00 +0800 Subject: [PATCH 3/4] vim-patch:9.0.0770: quickfix commands may keep memory allocated Problem: Quickfix commands may keep memory allocated. Solution: Free memory when it's a bit much. (Yegappan Lakshmanan, closes vim/vim#11379) https://github.com/vim/vim/commit/d8cd6f7427bc89aa38f42cc44f58bf5fb5f0f972 Co-authored-by: Yegappan Lakshmanan --- src/nvim/quickfix.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 51cc7fa30b..3c20c5239c 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -292,12 +292,26 @@ static garray_T *qfga_get(void) ga_init(&qfga, 1, 256); } - // Retain ga_data from previous use. Reset the length to zero. + // Reset the length to zero. Retain ga_data from previous use to avoid + // many alloc/free calls. qfga.ga_len = 0; return &qfga; } +/// The "qfga" grow array buffer is reused across multiple quickfix commands as +/// a temporary buffer to reduce the number of alloc/free calls. But if the +/// buffer size is large, then to avoid holding on to that memory, clear the +/// grow array. Otherwise just reset the grow array length. +static void qfga_clear(void) +{ + if (qfga.ga_maxlen > 1000) { + ga_clear(&qfga); + } else { + qfga.ga_len = 0; + } +} + // Counter to prevent autocmds from freeing up location lists when they are // still being used. static int quickfix_busy = 0; @@ -2847,6 +2861,8 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf msg_ext_set_kind("quickfix"); msg_attr_keep(gap->ga_data, 0, true, false); msg_scroll = (int)i; + + qfga_clear(); } /// Find a usable window for opening a file from the quickfix/location list. If @@ -3204,6 +3220,7 @@ void qf_list(exarg_T *eap) } os_breakcheck(); } + qfga_clear(); } /// Remove newlines and leading whitespace from an error message. @@ -4147,6 +4164,8 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q // Delete the empty line which is now at the end (void)ml_delete(lnum + 1, false); } + + qfga_clear(); } // Correct cursor position. From e0bbe8ccf88d4ad0667ef38ae48d8bfdabb0cc26 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 5 Mar 2023 17:04:53 +0800 Subject: [PATCH 4/4] vim-patch:9.0.0870: get E967 when using text property in quickfix window Problem: Get E967 when using text property in quickfix window. (Sergey Vlasov) Solution: Do not add an extra NUL and compute the text length correctly. (closes vim/vim#11513) https://github.com/vim/vim/commit/2f7bfe66a1373051792f2ecaeefb66049825221d Co-authored-by: Bram Moolenaar --- src/nvim/quickfix.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 3c20c5239c..9f6181f986 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3982,6 +3982,7 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli // for this entry, then use it. if (qftf_str != NULL && *qftf_str != NUL) { ga_concat(gap, qftf_str); + ga_append(gap, NUL); } else { buf_T *errbuf; if (qfp->qf_module != NULL) { @@ -4024,9 +4025,7 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli qf_fmt_text(gap, gap->ga_len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text); } - ga_append(gap, NUL); - - if (ml_append_buf(buf, lnum, gap->ga_data, gap->ga_len + 1, false) == FAIL) { + if (ml_append_buf(buf, lnum, gap->ga_data, gap->ga_len, false) == FAIL) { return FAIL; }