mirror of
https://github.com/neovim/neovim.git
synced 2025-10-06 18:06:30 +00:00
vim-patch:8.1.0252: quickfix functions are too long
Problem: Quickfix functions are too long.
Solution: Refactor. (Yegappan Lakshmanan, closes vim/vim#2950)
de3b3677f7
This commit is contained in:
@@ -163,6 +163,8 @@ enum {
|
||||
QF_MULTISCAN = 5,
|
||||
};
|
||||
|
||||
// State information used to parse lines and add entries to a quickfix/location
|
||||
// list.
|
||||
typedef struct {
|
||||
char_u *linebuf;
|
||||
size_t linelen;
|
||||
@@ -266,92 +268,95 @@ static struct fmtpattern
|
||||
};
|
||||
|
||||
// Convert an errorformat pattern to a regular expression pattern.
|
||||
// See fmt_pat definition above for the list of supported patterns.
|
||||
static char_u *fmtpat_to_regpat(
|
||||
const char_u *efmp,
|
||||
efm_T *fmt_ptr,
|
||||
// See fmt_pat definition above for the list of supported patterns. The
|
||||
// pattern specifier is supplied in "efmpat". The converted pattern is stored
|
||||
// in "regpat". Returns a pointer to the location after the pattern.
|
||||
static char_u * efmpat_to_regpat(
|
||||
const char_u *efmpat,
|
||||
char_u *regpat,
|
||||
efm_T *efminfo,
|
||||
int idx,
|
||||
int round,
|
||||
char_u *ptr,
|
||||
char_u *errmsg,
|
||||
size_t errmsglen)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (fmt_ptr->addr[idx]) {
|
||||
if (efminfo->addr[idx]) {
|
||||
// Each errorformat pattern can occur only once
|
||||
snprintf((char *)errmsg, errmsglen,
|
||||
_("E372: Too many %%%c in format string"), *efmp);
|
||||
_("E372: Too many %%%c in format string"), *efmpat);
|
||||
EMSG(errmsg);
|
||||
return NULL;
|
||||
}
|
||||
if ((idx && idx < 6
|
||||
&& vim_strchr((char_u *)"DXOPQ", fmt_ptr->prefix) != NULL)
|
||||
&& vim_strchr((char_u *)"DXOPQ", efminfo->prefix) != NULL)
|
||||
|| (idx == 6
|
||||
&& vim_strchr((char_u *)"OPQ", fmt_ptr->prefix) == NULL)) {
|
||||
&& vim_strchr((char_u *)"OPQ", efminfo->prefix) == NULL)) {
|
||||
snprintf((char *)errmsg, errmsglen,
|
||||
_("E373: Unexpected %%%c in format string"), *efmp);
|
||||
_("E373: Unexpected %%%c in format string"), *efmpat);
|
||||
EMSG(errmsg);
|
||||
return NULL;
|
||||
}
|
||||
fmt_ptr->addr[idx] = (char_u)++round;
|
||||
*ptr++ = '\\';
|
||||
*ptr++ = '(';
|
||||
efminfo->addr[idx] = (char_u)++round;
|
||||
*regpat++ = '\\';
|
||||
*regpat++ = '(';
|
||||
#ifdef BACKSLASH_IN_FILENAME
|
||||
if (*efmp == 'f') {
|
||||
if (*efmpat == 'f') {
|
||||
// Also match "c:" in the file name, even when
|
||||
// checking for a colon next: "%f:".
|
||||
// "\%(\a:\)\="
|
||||
STRCPY(ptr, "\\%(\\a:\\)\\=");
|
||||
ptr += 10;
|
||||
STRCPY(regpat, "\\%(\\a:\\)\\=");
|
||||
regpat += 10;
|
||||
}
|
||||
#endif
|
||||
if (*efmp == 'f' && efmp[1] != NUL) {
|
||||
if (efmp[1] != '\\' && efmp[1] != '%') {
|
||||
if (*efmpat == 'f' && efmpat[1] != NUL) {
|
||||
if (efmpat[1] != '\\' && efmpat[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;
|
||||
STRCPY(regpat, ".\\{-1,}");
|
||||
regpat += 7;
|
||||
} else {
|
||||
// File name followed by '\\' or '%': include as
|
||||
// many file name chars as possible.
|
||||
STRCPY(ptr, "\\f\\+");
|
||||
ptr += 4;
|
||||
STRCPY(regpat, "\\f\\+");
|
||||
regpat += 4;
|
||||
}
|
||||
} else {
|
||||
char_u *srcptr = (char_u *)fmt_pat[idx].pattern;
|
||||
while ((*ptr = *srcptr++) != NUL) {
|
||||
ptr++;
|
||||
while ((*regpat = *srcptr++) != NUL) {
|
||||
regpat++;
|
||||
}
|
||||
}
|
||||
*ptr++ = '\\';
|
||||
*ptr++ = ')';
|
||||
*regpat++ = '\\';
|
||||
*regpat++ = ')';
|
||||
|
||||
return ptr;
|
||||
return regpat;
|
||||
}
|
||||
|
||||
// Convert a scanf like format in 'errorformat' to a regular expression.
|
||||
static char_u *scanf_fmt_to_regpat(
|
||||
// Returns a pointer to the location after the pattern.
|
||||
static char_u * scanf_fmt_to_regpat(
|
||||
const char_u **pefmp,
|
||||
const char_u *efm,
|
||||
int len,
|
||||
const char_u **pefmp,
|
||||
char_u *ptr,
|
||||
char_u *regpat,
|
||||
char_u *errmsg,
|
||||
size_t errmsglen)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const char_u *efmp = *pefmp;
|
||||
|
||||
if (*++efmp == '[' || *efmp == '\\') {
|
||||
if ((*ptr++ = *efmp) == '[') { // %*[^a-z0-9] etc.
|
||||
if (*efmp == '[' || *efmp == '\\') {
|
||||
if ((*regpat++ = *efmp) == '[') { // %*[^a-z0-9] etc.
|
||||
if (efmp[1] == '^') {
|
||||
*ptr++ = *++efmp;
|
||||
*regpat++ = *++efmp;
|
||||
}
|
||||
if (efmp < efm + len) {
|
||||
*ptr++ = *++efmp; // could be ']'
|
||||
while (efmp < efm + len && (*ptr++ = *++efmp) != ']') {
|
||||
*regpat++ = *++efmp; // could be ']'
|
||||
while (efmp < efm + len && (*regpat++ = *++efmp) != ']') {
|
||||
}
|
||||
if (efmp == efm + len) {
|
||||
EMSG(_("E374: Missing ] in format string"));
|
||||
@@ -359,10 +364,10 @@ static char_u *scanf_fmt_to_regpat(
|
||||
}
|
||||
}
|
||||
} else if (efmp < efm + len) { // %*\D, %*\s etc.
|
||||
*ptr++ = *++efmp;
|
||||
*regpat++ = *++efmp;
|
||||
}
|
||||
*ptr++ = '\\';
|
||||
*ptr++ = '+';
|
||||
*regpat++ = '\\';
|
||||
*regpat++ = '+';
|
||||
} else {
|
||||
// TODO(vim): scanf()-like: %*ud, %*3c, %*f, ... ?
|
||||
snprintf((char *)errmsg, errmsglen,
|
||||
@@ -373,31 +378,27 @@ static char_u *scanf_fmt_to_regpat(
|
||||
|
||||
*pefmp = efmp;
|
||||
|
||||
return ptr;
|
||||
return regpat;
|
||||
}
|
||||
|
||||
// Analyze/parse an errorformat prefix.
|
||||
static int efm_analyze_prefix(const char_u **pefmp, efm_T *fmt_ptr,
|
||||
char_u *errmsg, size_t errmsglen)
|
||||
static char_u *efm_analyze_prefix(const char_u *efmp, efm_T *efminfo,
|
||||
char_u *errmsg, size_t errmsglen)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const char_u *efmp = *pefmp;
|
||||
|
||||
if (vim_strchr((char_u *)"+-", *efmp) != NULL) {
|
||||
fmt_ptr->flags = *efmp++;
|
||||
efminfo->flags = *efmp++;
|
||||
}
|
||||
if (vim_strchr((char_u *)"DXAEWICZGOPQ", *efmp) != NULL) {
|
||||
fmt_ptr->prefix = *efmp;
|
||||
efminfo->prefix = *efmp;
|
||||
} else {
|
||||
snprintf((char *)errmsg, errmsglen,
|
||||
_("E376: Invalid %%%c in format string prefix"), *efmp);
|
||||
EMSG(errmsg);
|
||||
return FAIL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*pefmp = efmp;
|
||||
|
||||
return OK;
|
||||
return efmp;
|
||||
}
|
||||
|
||||
|
||||
@@ -420,16 +421,17 @@ static int efm_to_regpat(const char_u *efm, int len, efm_T *fmt_ptr,
|
||||
}
|
||||
}
|
||||
if (idx < FMT_PATTERNS) {
|
||||
ptr = fmtpat_to_regpat(efmp, fmt_ptr, idx, round, ptr,
|
||||
errmsg, errmsglen);
|
||||
ptr = efmpat_to_regpat(efmp, ptr, fmt_ptr, idx, round, errmsg,
|
||||
errmsglen);
|
||||
if (ptr == NULL) {
|
||||
return -1;
|
||||
return FAIL;
|
||||
}
|
||||
round++;
|
||||
} else if (*efmp == '*') {
|
||||
ptr = scanf_fmt_to_regpat(efm, len, &efmp, ptr, errmsg, errmsglen);
|
||||
efmp++;
|
||||
ptr = scanf_fmt_to_regpat(&efmp, efm, len, ptr, errmsg, errmsglen);
|
||||
if (ptr == NULL) {
|
||||
return -1;
|
||||
return FAIL;
|
||||
}
|
||||
} else if (vim_strchr((char_u *)"%\\.^$~[", *efmp) != NULL) {
|
||||
*ptr++ = *efmp; // regexp magic characters
|
||||
@@ -438,14 +440,17 @@ static int efm_to_regpat(const char_u *efm, int len, efm_T *fmt_ptr,
|
||||
} else if (*efmp == '>') {
|
||||
fmt_ptr->conthere = true;
|
||||
} else if (efmp == efm + 1) { // analyse prefix
|
||||
if (efm_analyze_prefix(&efmp, fmt_ptr, errmsg, errmsglen) == FAIL) {
|
||||
return -1;
|
||||
// prefix is allowed only at the beginning of the errorformat
|
||||
// option part
|
||||
efmp = efm_analyze_prefix(efmp, fmt_ptr, errmsg, errmsglen);
|
||||
if (efmp == NULL) {
|
||||
return FAIL;
|
||||
}
|
||||
} else {
|
||||
snprintf((char *)errmsg, CMDBUFFSIZE + 1,
|
||||
_("E377: Invalid %%%c in format string"), *efmp);
|
||||
EMSG(errmsg);
|
||||
return -1;
|
||||
return FAIL;
|
||||
}
|
||||
} else { // copy normal character
|
||||
if (*efmp == '\\' && efmp + 1 < efm + len) {
|
||||
@@ -461,7 +466,7 @@ static int efm_to_regpat(const char_u *efm, int len, efm_T *fmt_ptr,
|
||||
*ptr++ = '$';
|
||||
*ptr = NUL;
|
||||
|
||||
return 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls
|
||||
@@ -477,7 +482,42 @@ static void free_efm_list(efm_T **efm_first)
|
||||
fmt_start = NULL;
|
||||
}
|
||||
|
||||
// Parse 'errorformat' option
|
||||
// Compute the size of the buffer used to convert a 'errorformat' pattern into
|
||||
// a regular expression pattern.
|
||||
static size_t efm_regpat_bufsz(char_u *efm)
|
||||
{
|
||||
size_t sz;
|
||||
|
||||
sz = (FMT_PATTERNS * 3) + (STRLEN(efm) << 2);
|
||||
for (int i = FMT_PATTERNS - 1; i >= 0; ) {
|
||||
sz += STRLEN(fmt_pat[i--].pattern);
|
||||
}
|
||||
#ifdef BACKSLASH_IN_FILENAME
|
||||
sz += 12; // "%f" can become twelve chars longer (see efm_to_regpat)
|
||||
#else
|
||||
sz += 2; // "%f" can become two chars longer
|
||||
#endif
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
// Return the length of a 'errorformat' option part (separated by ",").
|
||||
static int efm_option_part_len(char_u *efm)
|
||||
{
|
||||
int len;
|
||||
|
||||
for (len = 0; efm[len] != NUL && efm[len] != ','; len++) {
|
||||
if (efm[len] == '\\' && efm[len + 1] != NUL) {
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// Parse the 'errorformat' option. Multiple parts in the 'errorformat' option
|
||||
// are parsed and converted to regular expressions. Returns information about
|
||||
// the parsed 'errorformat' option.
|
||||
static efm_T * parse_efm_option(char_u *efm)
|
||||
{
|
||||
efm_T *fmt_ptr = NULL;
|
||||
@@ -489,16 +529,8 @@ static efm_T * parse_efm_option(char_u *efm)
|
||||
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 BACKSLASH_IN_FILENAME
|
||||
i += 12; // "%f" can become twelve chars longer (see efm_to_regpat)
|
||||
#else
|
||||
i += 2; // "%f" can become two chars longer
|
||||
#endif
|
||||
char_u *fmtstr = xmalloc(i);
|
||||
size_t sz = efm_regpat_bufsz(efm);
|
||||
char_u *fmtstr = xmalloc(sz);
|
||||
|
||||
while (efm[0] != NUL) {
|
||||
// Allocate a new eformat structure and put it at the end of the list
|
||||
@@ -511,13 +543,9 @@ static efm_T * parse_efm_option(char_u *efm)
|
||||
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++;
|
||||
}
|
||||
}
|
||||
len = efm_option_part_len(efm);
|
||||
|
||||
if (efm_to_regpat(efm, len, fmt_ptr, fmtstr, errmsg, errmsglen) == -1) {
|
||||
if (efm_to_regpat(efm, len, fmt_ptr, fmtstr, errmsg, errmsglen) == FAIL) {
|
||||
goto parse_efm_error;
|
||||
}
|
||||
if ((fmt_ptr->prog = vim_regcomp(fmtstr, RE_MAGIC + RE_STRING)) == NULL) {
|
||||
@@ -543,6 +571,7 @@ parse_efm_end:
|
||||
return fmt_first;
|
||||
}
|
||||
|
||||
// Allocate more memory for the line buffer used for parsing lines.
|
||||
static char_u *qf_grow_linebuf(qfstate_T *state, size_t newsz)
|
||||
{
|
||||
// If the line exceeds LINE_MAXLEN exclude the last
|
||||
@@ -1011,7 +1040,7 @@ qf_init_ext(
|
||||
} else {
|
||||
// Adding to existing list, use last entry.
|
||||
adding = true;
|
||||
if (qi->qf_lists[qf_idx].qf_count > 0) {
|
||||
if (!qf_list_empty(qi, qf_idx)) {
|
||||
old_last = qi->qf_lists[qf_idx].qf_last;
|
||||
}
|
||||
}
|
||||
@@ -1183,6 +1212,219 @@ static void qf_new_list(qf_info_T *qi, const char_u *qf_title)
|
||||
qi->qf_lists[qi->qf_curlist].qf_id = ++last_qf_id;
|
||||
}
|
||||
|
||||
// Parse the match for filename ('%f') pattern in regmatch.
|
||||
// Return the matched value in "fields->namebuf".
|
||||
static int qf_parse_fmt_f(regmatch_T *rmp,
|
||||
int midx,
|
||||
qffields_T *fields,
|
||||
int prefix)
|
||||
{
|
||||
char_u c;
|
||||
|
||||
if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
|
||||
// Expand ~/file and $HOME/file to full path.
|
||||
c = *rmp->endp[midx];
|
||||
*rmp->endp[midx] = NUL;
|
||||
expand_env(rmp->startp[midx], fields->namebuf, CMDBUFFSIZE);
|
||||
*rmp->endp[midx] = c;
|
||||
|
||||
// For separate filename patterns (%O, %P and %Q), the specified file
|
||||
// should exist.
|
||||
if (vim_strchr((char_u *)"OPQ", prefix) != NULL
|
||||
&& !os_path_exists(fields->namebuf)) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (rmp->startp[midx] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
fields->enr = (int)atol((char *)rmp->startp[midx]);
|
||||
return QF_OK;
|
||||
}
|
||||
|
||||
// Parse the match for line number (%l') pattern in regmatch.
|
||||
// Return the matched value in "fields->lnum".
|
||||
static int qf_parse_fmt_l(regmatch_T *rmp, int midx, qffields_T *fields)
|
||||
{
|
||||
if (rmp->startp[midx] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
fields->lnum = atol((char *)rmp->startp[midx]);
|
||||
return QF_OK;
|
||||
}
|
||||
|
||||
// Parse the match for column number ('%c') pattern in regmatch.
|
||||
// Return the matched value in "fields->col".
|
||||
static int qf_parse_fmt_c(regmatch_T *rmp, int midx, qffields_T *fields)
|
||||
{
|
||||
if (rmp->startp[midx] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
fields->col = (int)atol((char *)rmp->startp[midx]);
|
||||
return QF_OK;
|
||||
}
|
||||
|
||||
// Parse the match for error type ('%t') pattern in regmatch.
|
||||
// Return the matched value in "fields->type".
|
||||
static int qf_parse_fmt_t(regmatch_T *rmp, int midx, qffields_T *fields)
|
||||
{
|
||||
if (rmp->startp[midx] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
fields->type = *rmp->startp[midx];
|
||||
return QF_OK;
|
||||
}
|
||||
|
||||
// Parse the match for '%+' format pattern. The whole matching line is included
|
||||
// in the error string. Return the matched line in "fields->errmsg".
|
||||
static int qf_parse_fmt_plus(char_u *linebuf,
|
||||
size_t linelen,
|
||||
qffields_T *fields)
|
||||
{
|
||||
if (linelen >= fields->errmsglen) {
|
||||
// linelen + null terminator
|
||||
fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
|
||||
fields->errmsglen = linelen + 1;
|
||||
}
|
||||
STRLCPY(fields->errmsg, linebuf, linelen + 1);
|
||||
return QF_OK;
|
||||
}
|
||||
|
||||
// Parse the match for error message ('%m') pattern in regmatch.
|
||||
// Return the matched value in "fields->errmsg".
|
||||
static int qf_parse_fmt_m(regmatch_T *rmp, int midx, qffields_T *fields)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
len = (size_t)(rmp->endp[midx] - rmp->startp[midx]);
|
||||
if (len >= fields->errmsglen) {
|
||||
// len + null terminator
|
||||
fields->errmsg = xrealloc(fields->errmsg, len + 1);
|
||||
fields->errmsglen = len + 1;
|
||||
}
|
||||
STRLCPY(fields->errmsg, rmp->startp[midx], len + 1);
|
||||
return QF_OK;
|
||||
}
|
||||
|
||||
// Parse the match for rest of a single-line file message ('%r') pattern.
|
||||
// Return the matched value in "tail".
|
||||
static int qf_parse_fmt_r(regmatch_T *rmp, int midx, char_u **tail)
|
||||
{
|
||||
if (rmp->startp[midx] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
*tail = rmp->startp[midx];
|
||||
return QF_OK;
|
||||
}
|
||||
|
||||
|
||||
// Parse the match for the pointer line ('%p') pattern in regmatch.
|
||||
// Return the matched value in "fields->col".
|
||||
static int qf_parse_fmt_p(regmatch_T *rmp, int midx, qffields_T *fields)
|
||||
{
|
||||
char_u *match_ptr;
|
||||
|
||||
if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
fields->col = 0;
|
||||
for (match_ptr = rmp->startp[midx]; match_ptr != rmp->endp[midx];
|
||||
match_ptr++) {
|
||||
fields->col++;
|
||||
if (*match_ptr == TAB) {
|
||||
fields->col += 7;
|
||||
fields->col -= fields->col % 8;
|
||||
}
|
||||
}
|
||||
fields->col++;
|
||||
fields->use_viscol = true;
|
||||
return QF_OK;
|
||||
}
|
||||
|
||||
|
||||
// Parse the match for the virtual column number ('%v') pattern in regmatch.
|
||||
// Return the matched value in "fields->col".
|
||||
static int qf_parse_fmt_v(regmatch_T *rmp, int midx, qffields_T *fields)
|
||||
{
|
||||
if (rmp->startp[midx] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
fields->col = (int)atol((char *)rmp->startp[midx]);
|
||||
fields->use_viscol = true;
|
||||
return QF_OK;
|
||||
}
|
||||
|
||||
|
||||
// Parse the match for the search text ('%s') pattern in regmatch.
|
||||
// Return the matched value in "fields->pattern".
|
||||
static int qf_parse_fmt_s(regmatch_T *rmp, int midx, qffields_T *fields)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
len = (size_t)(rmp->endp[midx] - rmp->startp[midx]);
|
||||
if (len > CMDBUFFSIZE - 5) {
|
||||
len = CMDBUFFSIZE - 5;
|
||||
}
|
||||
STRCPY(fields->pattern, "^\\V");
|
||||
xstrlcat((char *)fields->pattern, (char *)rmp->startp[midx], len + 4);
|
||||
fields->pattern[len + 3] = '\\';
|
||||
fields->pattern[len + 4] = '$';
|
||||
fields->pattern[len + 5] = NUL;
|
||||
return QF_OK;
|
||||
}
|
||||
|
||||
// Parse the match for the module ('%o') pattern in regmatch.
|
||||
// Return the matched value in "fields->module".
|
||||
static int qf_parse_fmt_o(regmatch_T *rmp, int midx, qffields_T *fields)
|
||||
{
|
||||
size_t len;
|
||||
size_t dsize;
|
||||
|
||||
if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
len = (size_t)(rmp->endp[midx] - rmp->startp[midx]);
|
||||
dsize = STRLEN(fields->module) + len + 1;
|
||||
if (dsize > CMDBUFFSIZE) {
|
||||
dsize = CMDBUFFSIZE;
|
||||
}
|
||||
xstrlcat((char *)fields->module, (char *)rmp->startp[midx], dsize);
|
||||
return QF_OK;
|
||||
}
|
||||
|
||||
// 'errorformat' format pattern parser functions.
|
||||
// The '%f' and '%r' formats are parsed differently from other formats.
|
||||
// See qf_parse_match() for details.
|
||||
static int (*qf_parse_fmt[FMT_PATTERNS])(regmatch_T *, int, qffields_T *) = {
|
||||
NULL,
|
||||
qf_parse_fmt_n,
|
||||
qf_parse_fmt_l,
|
||||
qf_parse_fmt_c,
|
||||
qf_parse_fmt_t,
|
||||
qf_parse_fmt_m,
|
||||
NULL,
|
||||
qf_parse_fmt_p,
|
||||
qf_parse_fmt_v,
|
||||
qf_parse_fmt_s,
|
||||
qf_parse_fmt_o
|
||||
};
|
||||
|
||||
/// Parse the error format matches in 'regmatch' and set the values in 'fields'.
|
||||
/// fmt_ptr contains the 'efm' format specifiers/prefixes that have a match.
|
||||
/// Returns QF_OK if all the matches are successfully parsed. On failure,
|
||||
@@ -1193,7 +1435,8 @@ static int qf_parse_match(char_u *linebuf, size_t linelen, efm_T *fmt_ptr,
|
||||
{
|
||||
char_u idx = fmt_ptr->prefix;
|
||||
int i;
|
||||
size_t len;
|
||||
int midx;
|
||||
int status;
|
||||
|
||||
if ((idx == 'C' || idx == 'Z') && !qf_multiline) {
|
||||
return QF_FAIL;
|
||||
@@ -1207,118 +1450,26 @@ static int qf_parse_match(char_u *linebuf, size_t linelen, efm_T *fmt_ptr,
|
||||
// Extract error message data from matched line.
|
||||
// We check for an actual submatch, because "\[" and "\]" in
|
||||
// the 'errorformat' may cause the wrong submatch to be used.
|
||||
if ((i = (int)fmt_ptr->addr[0]) > 0) { // %f
|
||||
if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
|
||||
// Expand ~/file and $HOME/file to full path.
|
||||
char_u c = *regmatch->endp[i];
|
||||
*regmatch->endp[i] = NUL;
|
||||
expand_env(regmatch->startp[i], fields->namebuf, CMDBUFFSIZE);
|
||||
*regmatch->endp[i] = c;
|
||||
|
||||
if (vim_strchr((char_u *)"OPQ", idx) != NULL
|
||||
&& !os_path_exists(fields->namebuf)) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
}
|
||||
if ((i = (int)fmt_ptr->addr[1]) > 0) { // %n
|
||||
if (regmatch->startp[i] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
fields->enr = (int)atol((char *)regmatch->startp[i]);
|
||||
}
|
||||
if ((i = (int)fmt_ptr->addr[2]) > 0) { // %l
|
||||
if (regmatch->startp[i] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
fields->lnum = atol((char *)regmatch->startp[i]);
|
||||
}
|
||||
if ((i = (int)fmt_ptr->addr[3]) > 0) { // %c
|
||||
if (regmatch->startp[i] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
fields->col = (int)atol((char *)regmatch->startp[i]);
|
||||
}
|
||||
if ((i = (int)fmt_ptr->addr[4]) > 0) { // %t
|
||||
if (regmatch->startp[i] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
fields->type = *regmatch->startp[i];
|
||||
}
|
||||
if (fmt_ptr->flags == '+' && !qf_multiscan) { // %+
|
||||
if (linelen >= fields->errmsglen) {
|
||||
// linelen + null terminator
|
||||
fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
|
||||
fields->errmsglen = linelen + 1;
|
||||
}
|
||||
STRLCPY(fields->errmsg, linebuf, linelen + 1);
|
||||
} else if ((i = (int)fmt_ptr->addr[5]) > 0) { // %m
|
||||
if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
len = (size_t)(regmatch->endp[i] - regmatch->startp[i]);
|
||||
if (len >= fields->errmsglen) {
|
||||
// len + null terminator
|
||||
fields->errmsg = xrealloc(fields->errmsg, len + 1);
|
||||
fields->errmsglen = len + 1;
|
||||
}
|
||||
STRLCPY(fields->errmsg, regmatch->startp[i], len + 1);
|
||||
}
|
||||
if ((i = (int)fmt_ptr->addr[6]) > 0) { // %r
|
||||
if (regmatch->startp[i] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
*tail = regmatch->startp[i];
|
||||
}
|
||||
if ((i = (int)fmt_ptr->addr[7]) > 0) { // %p
|
||||
if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
fields->col = 0;
|
||||
char_u *match_ptr;
|
||||
for (match_ptr = regmatch->startp[i]; match_ptr != regmatch->endp[i];
|
||||
match_ptr++) {
|
||||
fields->col++;
|
||||
if (*match_ptr == TAB) {
|
||||
fields->col += 7;
|
||||
fields->col -= fields->col % 8;
|
||||
for (i = 0; i < FMT_PATTERNS; i++) {
|
||||
status = QF_OK;
|
||||
midx = (int)fmt_ptr->addr[i];
|
||||
if (i == 0 && midx > 0) { // %f
|
||||
status = qf_parse_fmt_f(regmatch, midx, fields, idx);
|
||||
} else if (i == 5) {
|
||||
if (fmt_ptr->flags == '+' && !qf_multiscan) { // %+
|
||||
status = qf_parse_fmt_plus(linebuf, linelen, fields);
|
||||
} else if (midx > 0) { // %m
|
||||
status = qf_parse_fmt_m(regmatch, midx, fields);
|
||||
}
|
||||
} else if (i == 6 && midx > 0) { // %r
|
||||
status = qf_parse_fmt_r(regmatch, midx, tail);
|
||||
} else if (midx > 0) { // others
|
||||
status = (qf_parse_fmt[i])(regmatch, midx, fields);
|
||||
}
|
||||
fields->col++;
|
||||
fields->use_viscol = true;
|
||||
}
|
||||
if ((i = (int)fmt_ptr->addr[8]) > 0) { // %v
|
||||
if (regmatch->startp[i] == NULL) {
|
||||
return QF_FAIL;
|
||||
|
||||
if (status != QF_OK) {
|
||||
return status;
|
||||
}
|
||||
fields->col = (int)atol((char *)regmatch->startp[i]);
|
||||
fields->use_viscol = true;
|
||||
}
|
||||
if ((i = (int)fmt_ptr->addr[9]) > 0) { // %s
|
||||
if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
len = (size_t)(regmatch->endp[i] - regmatch->startp[i]);
|
||||
if (len > CMDBUFFSIZE - 5) {
|
||||
len = CMDBUFFSIZE - 5;
|
||||
}
|
||||
STRCPY(fields->pattern, "^\\V");
|
||||
STRNCAT(fields->pattern, regmatch->startp[i], len);
|
||||
fields->pattern[len + 3] = '\\';
|
||||
fields->pattern[len + 4] = '$';
|
||||
fields->pattern[len + 5] = NUL;
|
||||
}
|
||||
if ((i = (int)fmt_ptr->addr[10]) > 0) { // %o
|
||||
if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
|
||||
return QF_FAIL;
|
||||
}
|
||||
len = (size_t)(regmatch->endp[i] - regmatch->startp[i]);
|
||||
if (len > CMDBUFFSIZE) {
|
||||
len = CMDBUFFSIZE;
|
||||
}
|
||||
STRNCAT(fields->module, regmatch->startp[i], len);
|
||||
}
|
||||
|
||||
return QF_OK;
|
||||
@@ -1572,7 +1723,7 @@ static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname,
|
||||
qfp->qf_valid = valid;
|
||||
|
||||
lastp = &qi->qf_lists[qf_idx].qf_last;
|
||||
if (qi->qf_lists[qf_idx].qf_count == 0) {
|
||||
if (qf_list_empty(qi, qf_idx)) {
|
||||
// first element in the list
|
||||
qi->qf_lists[qf_idx].qf_start = qfp;
|
||||
qi->qf_lists[qf_idx].qf_ptr = qfp;
|
||||
@@ -2633,22 +2784,106 @@ theend:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Highlight attributes used for displaying entries from the quickfix list.
|
||||
static int qfFileAttr;
|
||||
static int qfSepAttr;
|
||||
static int qfLineAttr;
|
||||
|
||||
// Display information about a single entry from the quickfix/location list.
|
||||
// Used by ":clist/:llist" commands.
|
||||
static void qf_list_entry(qf_info_T *qi, qfline_T *qfp, int qf_idx)
|
||||
{
|
||||
char_u *fname;
|
||||
buf_T *buf;
|
||||
|
||||
fname = NULL;
|
||||
if (qfp->qf_module != NULL && *qfp->qf_module != NUL) {
|
||||
vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", qf_idx,
|
||||
(char *)qfp->qf_module);
|
||||
} else {
|
||||
if (qfp->qf_fnum != 0
|
||||
&& (buf = buflist_findnr(qfp->qf_fnum)) != NULL) {
|
||||
fname = buf->b_fname;
|
||||
if (qfp->qf_type == 1) { // :helpgrep
|
||||
fname = path_tail(fname);
|
||||
}
|
||||
}
|
||||
if (fname == NULL) {
|
||||
snprintf((char *)IObuff, IOSIZE, "%2d", qf_idx);
|
||||
} else {
|
||||
vim_snprintf((char *)IObuff, IOSIZE, "%2d %s",
|
||||
qf_idx, (char *)fname);
|
||||
}
|
||||
}
|
||||
|
||||
// Support for filtering entries using :filter /pat/ clist
|
||||
// Match against the module name, file name, search pattern and
|
||||
// text of the entry.
|
||||
bool filter_entry = true;
|
||||
if (qfp->qf_module != NULL && *qfp->qf_module != NUL) {
|
||||
filter_entry &= message_filtered(qfp->qf_module);
|
||||
}
|
||||
if (filter_entry && fname != NULL) {
|
||||
filter_entry &= message_filtered(fname);
|
||||
}
|
||||
if (filter_entry && qfp->qf_pattern != NULL) {
|
||||
filter_entry &= message_filtered(qfp->qf_pattern);
|
||||
}
|
||||
if (filter_entry) {
|
||||
filter_entry &= message_filtered(qfp->qf_text);
|
||||
}
|
||||
if (filter_entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg_putchar('\n');
|
||||
msg_outtrans_attr(IObuff, qf_idx == qi->qf_lists[qi->qf_curlist].qf_index
|
||||
? HL_ATTR(HLF_QFL) : qfFileAttr);
|
||||
|
||||
if (qfp->qf_lnum != 0) {
|
||||
msg_puts_attr(":", qfSepAttr);
|
||||
}
|
||||
if (qfp->qf_lnum == 0) {
|
||||
IObuff[0] = NUL;
|
||||
} else if (qfp->qf_col == 0) {
|
||||
vim_snprintf((char *)IObuff, IOSIZE, "%" PRIdLINENR, qfp->qf_lnum);
|
||||
} else {
|
||||
vim_snprintf((char *)IObuff, IOSIZE, "%" PRIdLINENR " col %d",
|
||||
qfp->qf_lnum, qfp->qf_col);
|
||||
}
|
||||
vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE, "%s",
|
||||
(char *)qf_types(qfp->qf_type, qfp->qf_nr));
|
||||
msg_puts_attr((const char *)IObuff, qfLineAttr);
|
||||
msg_puts_attr(":", qfSepAttr);
|
||||
if (qfp->qf_pattern != NULL) {
|
||||
qf_fmt_text(qfp->qf_pattern, IObuff, IOSIZE);
|
||||
msg_puts((const char *)IObuff);
|
||||
msg_puts_attr(":", qfSepAttr);
|
||||
}
|
||||
msg_puts(" ");
|
||||
|
||||
// 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,
|
||||
IObuff, IOSIZE);
|
||||
msg_prt_line(IObuff, false);
|
||||
ui_flush(); // show one line at a time
|
||||
}
|
||||
|
||||
/*
|
||||
* ":clist": list all errors
|
||||
* ":llist": list all locations
|
||||
*/
|
||||
void qf_list(exarg_T *eap)
|
||||
{
|
||||
buf_T *buf;
|
||||
char_u *fname;
|
||||
qfline_T *qfp;
|
||||
int i;
|
||||
int idx1 = 1;
|
||||
int idx2 = -1;
|
||||
char_u *arg = eap->arg;
|
||||
int qfFileAttr;
|
||||
int qfSepAttr;
|
||||
int qfLineAttr;
|
||||
int all = eap->forceit; // if not :cl!, only show
|
||||
// recognised errors
|
||||
qf_info_T *qi = &ql_info;
|
||||
@@ -2717,80 +2952,9 @@ void qf_list(exarg_T *eap)
|
||||
break;
|
||||
}
|
||||
|
||||
fname = NULL;
|
||||
if (qfp->qf_module != NULL && *qfp->qf_module != NUL) {
|
||||
vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", i,
|
||||
(char *)qfp->qf_module);
|
||||
} else {
|
||||
if (qfp->qf_fnum != 0 && (buf = buflist_findnr(qfp->qf_fnum)) != NULL) {
|
||||
fname = buf->b_fname;
|
||||
if (qfp->qf_type == 1) { // :helpgrep
|
||||
fname = path_tail(fname);
|
||||
}
|
||||
}
|
||||
if (fname == NULL) {
|
||||
snprintf((char *)IObuff, IOSIZE, "%2d", i);
|
||||
} else {
|
||||
vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", i, (char *)fname);
|
||||
}
|
||||
}
|
||||
|
||||
// Support for filtering entries using :filter /pat/ clist
|
||||
// Match against the module name, file name, search pattern and
|
||||
// text of the entry.
|
||||
bool filter_entry = true;
|
||||
if (qfp->qf_module != NULL && *qfp->qf_module != NUL) {
|
||||
filter_entry &= message_filtered(qfp->qf_module);
|
||||
}
|
||||
if (filter_entry && fname != NULL) {
|
||||
filter_entry &= message_filtered(fname);
|
||||
}
|
||||
if (filter_entry && qfp->qf_pattern != NULL) {
|
||||
filter_entry &= message_filtered(qfp->qf_pattern);
|
||||
}
|
||||
if (filter_entry) {
|
||||
filter_entry &= message_filtered(qfp->qf_text);
|
||||
}
|
||||
if (filter_entry) {
|
||||
goto next_entry;
|
||||
}
|
||||
msg_putchar('\n');
|
||||
msg_outtrans_attr(IObuff, i == qi->qf_lists[qi->qf_curlist].qf_index
|
||||
? HL_ATTR(HLF_QFL) : qfFileAttr);
|
||||
|
||||
if (qfp->qf_lnum != 0) {
|
||||
msg_puts_attr(":", qfSepAttr);
|
||||
}
|
||||
if (qfp->qf_lnum == 0) {
|
||||
IObuff[0] = NUL;
|
||||
} else if (qfp->qf_col == 0) {
|
||||
vim_snprintf((char *)IObuff, IOSIZE, "%" PRIdLINENR, qfp->qf_lnum);
|
||||
} else {
|
||||
vim_snprintf((char *)IObuff, IOSIZE, "%" PRIdLINENR " col %d",
|
||||
qfp->qf_lnum, qfp->qf_col);
|
||||
}
|
||||
vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE, "%s",
|
||||
(char *)qf_types(qfp->qf_type, qfp->qf_nr));
|
||||
msg_puts_attr((const char *)IObuff, qfLineAttr);
|
||||
msg_puts_attr(":", qfSepAttr);
|
||||
if (qfp->qf_pattern != NULL) {
|
||||
qf_fmt_text(qfp->qf_pattern, IObuff, IOSIZE);
|
||||
msg_puts((const char *)IObuff);
|
||||
msg_puts_attr(":", qfSepAttr);
|
||||
}
|
||||
msg_puts(" ");
|
||||
|
||||
/* 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,
|
||||
IObuff, IOSIZE);
|
||||
msg_prt_line(IObuff, FALSE);
|
||||
ui_flush(); /* show one line at a time */
|
||||
qf_list_entry(qi, qfp, i);
|
||||
}
|
||||
|
||||
next_entry:
|
||||
qfp = qfp->qf_next;
|
||||
if (qfp == NULL) {
|
||||
break;
|
||||
@@ -2995,8 +3159,8 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount,
|
||||
qi = wp->w_llist;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < qi->qf_listcount; ++idx)
|
||||
if (qi->qf_lists[idx].qf_count)
|
||||
for (idx = 0; idx < qi->qf_listcount; idx++) {
|
||||
if (!qf_list_empty(qi, idx)) {
|
||||
for (i = 0, qfp = qi->qf_lists[idx].qf_start;
|
||||
i < qi->qf_lists[idx].qf_count && qfp != NULL;
|
||||
i++, qfp = qfp->qf_next) {
|
||||
@@ -3011,6 +3175,8 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount,
|
||||
qfp->qf_lnum += amount_after;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found_one;
|
||||
}
|
||||
@@ -4596,7 +4762,7 @@ void ex_vimgrep(exarg_T *eap)
|
||||
}
|
||||
|
||||
// Jump to first match.
|
||||
if (qi->qf_lists[qi->qf_curlist].qf_count > 0) {
|
||||
if (!qf_list_empty(qi, qi->qf_curlist)) {
|
||||
if ((flags & VGR_NOJUMP) == 0) {
|
||||
vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy, first_match_buf,
|
||||
target_dir);
|
||||
@@ -4805,7 +4971,7 @@ int get_errorlist(const qf_info_T *qi_arg, win_T *wp, int qf_idx, list_T *list)
|
||||
}
|
||||
|
||||
if (qf_idx >= qi->qf_listcount
|
||||
|| qi->qf_lists[qf_idx].qf_count == 0) {
|
||||
|| qf_list_empty(qi, qf_idx)) {
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
@@ -5119,7 +5285,7 @@ static int qf_getprop_ctx(qf_info_T *qi, int qf_idx, dict_T *retdict)
|
||||
static int qf_getprop_idx(qf_info_T *qi, int qf_idx, dict_T *retdict)
|
||||
{
|
||||
int idx = qi->qf_lists[qf_idx].qf_index;
|
||||
if (qi->qf_lists[qf_idx].qf_count == 0) {
|
||||
if (qf_list_empty(qi, qf_idx)) {
|
||||
// For empty lists, qf_index is set to 1
|
||||
idx = 0;
|
||||
}
|
||||
@@ -5276,7 +5442,7 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list,
|
||||
// make place for a new list
|
||||
qf_new_list(qi, title);
|
||||
qf_idx = qi->qf_curlist;
|
||||
} else if (action == 'a' && qi->qf_lists[qf_idx].qf_count > 0) {
|
||||
} else if (action == 'a' && !qf_list_empty(qi, qf_idx)) {
|
||||
// Adding to existing list, use last entry.
|
||||
old_last = qi->qf_lists[qf_idx].qf_last;
|
||||
} else if (action == 'r') {
|
||||
@@ -5308,7 +5474,7 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list,
|
||||
}
|
||||
if (action != 'a') {
|
||||
qi->qf_lists[qf_idx].qf_ptr = qi->qf_lists[qf_idx].qf_start;
|
||||
if (qi->qf_lists[qf_idx].qf_count > 0) {
|
||||
if (!qf_list_empty(qi, qf_idx)) {
|
||||
qi->qf_lists[qf_idx].qf_index = 1;
|
||||
}
|
||||
}
|
||||
@@ -6023,11 +6189,12 @@ void ex_helpgrep(exarg_T *eap)
|
||||
}
|
||||
}
|
||||
|
||||
/* Jump to first match. */
|
||||
if (qi->qf_lists[qi->qf_curlist].qf_count > 0)
|
||||
qf_jump(qi, 0, 0, FALSE);
|
||||
else
|
||||
// Jump to first match.
|
||||
if (!qf_list_empty(qi, qi->qf_curlist)) {
|
||||
qf_jump(qi, 0, 0, false);
|
||||
} else {
|
||||
EMSG2(_(e_nomatch2), eap->arg);
|
||||
}
|
||||
|
||||
if (eap->cmdidx == CMD_lhelpgrep) {
|
||||
// If the help window is not opened or if it already points to the
|
||||
|
@@ -1045,8 +1045,8 @@ func Test_efm2()
|
||||
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)
|
||||
call assert_equal('^\VLine search text\$', l[0].pattern)
|
||||
call assert_equal(0, l[0].lnum)
|
||||
|
||||
let l = split(execute('clist', ''), "\n")
|
||||
call assert_equal([' 1 Xtestfile:^\VLine search text\$: '], l)
|
||||
|
Reference in New Issue
Block a user