From f2fc44550fbe5b7ebfedc2b155dc41e93f49aedb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 26 Oct 2023 07:42:29 +0800 Subject: [PATCH] vim-patch:9.0.2064: cannot use buffer-number for errorformat (#25782) Problem: cannot use buffer-number for errorformat Solution: add support for parsing a buffer number using '%b' in 'errorformat' closes: vim/vim#13419 https://github.com/vim/vim/commit/b731800522af00fd348814d33a065b92e698afc3 Co-authored-by: Yegappan Lakshmanan --- runtime/doc/quickfix.txt | 6 ++++ src/nvim/quickfix.c | 51 ++++++++++++++++++++---------- test/old/testdir/test_quickfix.vim | 49 ++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 16 deletions(-) diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 0bf04034ec..4428ff2f65 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -1381,6 +1381,7 @@ rest is ignored. Items can only be 1023 bytes long. Basic items %f file name (finds a string) + %b buffer number (finds a number) %o module name (finds a string) %l line number (finds a number) %e end line number (finds a number) @@ -1420,6 +1421,11 @@ On Windows a leading "C:" will be included in "%f", even when using "%f:". This means that a file name which is a single alphabetical letter will not be detected. +The "%b" conversion is used to parse a buffer number. This is useful for +referring to lines in a scratch buffer or a buffer with no name. If a buffer +with the matching number doesn't exist, then that line is used as a non-error +line. + The "%p" conversion is normally followed by a "^". It's used for compilers that output a line like: > ^ diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index be2681e765..eae357ebf0 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -152,7 +152,7 @@ struct qf_info_S { static qf_info_T ql_info; // global quickfix list static unsigned last_qf_id = 0; // Last Used quickfix list id -#define FMT_PATTERNS 13 // maximum number of % recognized +#define FMT_PATTERNS 14 // maximum number of % recognized // Structure used to hold the info of one part of 'errorformat' typedef struct efm_S efm_T; @@ -217,6 +217,7 @@ typedef struct { typedef struct { char *namebuf; + int bnr; char *module; char *errmsg; size_t errmsglen; @@ -344,7 +345,7 @@ static int qf_init_process_nextline(qf_list_T *qfl, efm_T *fmt_first, qfstate_T : ((qfl->qf_currfile != NULL && fields->valid) ? qfl->qf_currfile : NULL), fields->module, - 0, + fields->bnr, fields->errmsg, fields->lnum, fields->end_lnum, @@ -391,20 +392,21 @@ static struct fmtpattern { char *pattern; } fmt_pat[FMT_PATTERNS] = { { 'f', ".\\+" }, // only used when at end - { 'n', "\\d\\+" }, // 1 - { 'l', "\\d\\+" }, // 2 - { 'e', "\\d\\+" }, // 3 - { 'c', "\\d\\+" }, // 4 - { 'k', "\\d\\+" }, // 5 - { 't', "." }, // 6 -#define FMT_PATTERN_M 7 - { 'm', ".\\+" }, // 7 -#define FMT_PATTERN_R 8 - { 'r', ".*" }, // 8 - { 'p', "[- \t.]*" }, // 9 - { 'v', "\\d\\+" }, // 10 - { 's', ".\\+" }, // 11 - { 'o', ".\\+" } // 12 + { 'b', "\\d\\+" }, // 1 + { 'n', "\\d\\+" }, // 2 + { 'l', "\\d\\+" }, // 3 + { 'e', "\\d\\+" }, // 4 + { 'c', "\\d\\+" }, // 5 + { 'k', "\\d\\+" }, // 6 + { 't', "." }, // 7 +#define FMT_PATTERN_M 8 + { 'm', ".\\+" }, // 8 +#define FMT_PATTERN_R 9 + { 'r', ".*" }, // 9 + { 'p', "[-\t .]*" }, // 10 + { 'v', "\\d\\+" }, // 11 + { 's', ".\\+" }, // 12 + { 'o', ".\\+" } // 13 }; /// Convert an errorformat pattern to a regular expression pattern. @@ -1312,6 +1314,21 @@ static int qf_parse_fmt_f(regmatch_T *rmp, int midx, qffields_T *fields, int pre return QF_OK; } +/// Parse the match for buffer number ('%b') pattern in regmatch. +/// Return the matched value in "fields->bnr". +static int qf_parse_fmt_b(regmatch_T *rmp, int midx, qffields_T *fields) +{ + if (rmp->startp[midx] == NULL) { + return QF_FAIL; + } + int bnr = (int)atol(rmp->startp[midx]); + if (buflist_findnr(bnr) == NULL) { + return QF_FAIL; + } + fields->bnr = bnr; + return QF_OK; +} + /// Parse the match for error number ('%n') pattern in regmatch. /// Return the matched value in "fields->enr". static int qf_parse_fmt_n(regmatch_T *rmp, int midx, qffields_T *fields) @@ -1496,6 +1513,7 @@ static int qf_parse_fmt_o(regmatch_T *rmp, int midx, qffields_T *fields) /// Keep in sync with fmt_pat[]. static int (*qf_parse_fmt[FMT_PATTERNS])(regmatch_T *, int, qffields_T *) = { NULL, // %f + qf_parse_fmt_b, qf_parse_fmt_n, qf_parse_fmt_l, qf_parse_fmt_e, @@ -1568,6 +1586,7 @@ static int qf_parse_get_fields(char *linebuf, size_t linelen, efm_T *fmt_ptr, qf } fields->namebuf[0] = NUL; + fields->bnr = 0; fields->module[0] = NUL; fields->pattern[0] = NUL; if (!qf_multiscan) { diff --git a/test/old/testdir/test_quickfix.vim b/test/old/testdir/test_quickfix.vim index ebf1bfdc64..b5abb08ed8 100644 --- a/test/old/testdir/test_quickfix.vim +++ b/test/old/testdir/test_quickfix.vim @@ -6320,4 +6320,53 @@ func Test_quickfix_buffer_contents() call setqflist([], 'f') endfunc +" Test for "%b" in "errorformat" +func Test_efm_format_b() + call setqflist([], 'f') + new + call setline(1, ['1: abc', '1: def', '1: ghi']) + let b1 = bufnr() + new + call setline(1, ['2: abc', '2: def', '2: ghi']) + let b2 = bufnr() + new + call setline(1, ['3: abc', '3: def', '3: ghi']) + let b3 = bufnr() + new + let lines =<< trim eval END + {b1}:1:1 + {b2}:2:2 + {b3}:3:3 + END + call setqflist([], ' ', #{lines: lines, efm: '%b:%l:%c'}) + cfirst + call assert_equal([b1, 1, 1], [bufnr(), line('.'), col('.')]) + cnext + call assert_equal([b2, 2, 2], [bufnr(), line('.'), col('.')]) + cnext + call assert_equal([b3, 3, 3], [bufnr(), line('.'), col('.')]) + enew! + + " Use a non-existing buffer + let lines =<< trim eval END + 9991:1:1:m1 + 9992:2:2:m2 + {b3}:3:3:m3 + END + call setqflist([], ' ', #{lines: lines, efm: '%b:%l:%c:%m'}) + cfirst | cnext + call assert_equal([b3, 3, 3], [bufnr(), line('.'), col('.')]) + " Lines with non-existing buffer numbers should be used as non-error lines + call assert_equal([ + \ #{lnum: 0, bufnr: 0, end_lnum: 0, pattern: '', valid: 0, vcol: 0, nr: -1, + \ module: '', type: '', end_col: 0, col: 0, text: '9991:1:1:m1'}, + \ #{lnum: 0, bufnr: 0, end_lnum: 0, pattern: '', valid: 0, vcol: 0, nr: -1, + \ module: '', type: '', end_col: 0, col: 0, text: '9992:2:2:m2'}, + \ #{lnum: 3, bufnr: b3, end_lnum: 0, pattern: '', valid: 1, vcol: 0, + \ nr: -1, module: '', type: '', end_col: 0, col: 3, text: 'm3'}], + \ getqflist()) + %bw! + call setqflist([], 'f') +endfunc + " vim: shiftwidth=2 sts=2 expandtab