📄 quickfix.c
字号:
/* vi:set ts=8 sts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* quickfix.c: functions for quickfix mode, using a file with error messages
*/
#include "vim.h"
#ifdef QUICKFIX
static void qf_msg __ARGS((void));
static void qf_free __ARGS((int idx));
static char_u *qf_types __ARGS((int, int));
/*
* for each error the next struct is allocated and linked in a list
*/
struct qf_line
{
struct qf_line *qf_next; /* pointer to next error in the list */
struct qf_line *qf_prev; /* pointer to previous error in the list */
linenr_t qf_lnum; /* line number where the error occurred */
int qf_fnum; /* file number for the line */
int qf_col; /* column where the error occurred */
int qf_nr; /* error number */
char_u *qf_text; /* description of the error */
char_u qf_cleared;/* set to TRUE if line has been deleted */
char_u qf_type; /* type of the error (mostly 'E') */
char_u qf_valid; /* valid error message detected */
};
/*
* There is a stack of error lists.
*/
#define LISTCOUNT 10
struct qf_list
{
struct qf_line *qf_start; /* pointer to the first error */
struct qf_line *qf_ptr; /* pointer to the current error */
int qf_count; /* number of errors (0 means no error list) */
int qf_index; /* current index in the error list */
int qf_nonevalid; /* TRUE if not a single valid entry found */
} qf_lists[LISTCOUNT];
static int qf_curlist = 0; /* current error list */
static int qf_listcount = 0; /* current number of lists */
#define MAX_ADDR 7 /* maximum number of % recognized, also adjust
sscanf() below */
/*
* Structure used to hold the info of one part of 'errorformat'
*/
struct eformat
{
char_u *fmtstr; /* pre-formatted part of 'errorformat' */
#ifdef UTS2
char_u *(adr[MAX_ADDR]); /* addresses used */
#else
void *(adr[MAX_ADDR]);
#endif
int adr_cnt; /* number of addresses used */
struct eformat *next; /* pointer to next (NULL if last) */
};
/*
* Read the errorfile into memory, line by line, building the error list.
* Return -1 for error, number of errors for success.
*/
int
qf_init(efile, errorformat)
char_u *efile;
char_u *errorformat;
{
char_u *namebuf;
char_u *errmsg;
int col;
int type;
int valid;
long lnum;
int enr;
FILE *fd;
struct qf_line *qfp = NULL;
struct qf_line *qfprev = NULL; /* init to make SASC shut up */
char_u *efmp;
struct eformat *fmt_first = NULL;
struct eformat *fmt_last = NULL;
struct eformat *fmt_ptr;
char_u *efm;
int maxlen;
int len;
int i, j;
int retval = -1; /* default: return error flag */
if (efile == NULL)
return FAIL;
namebuf = alloc(CMDBUFFSIZE + 1);
errmsg = alloc(CMDBUFFSIZE + 1);
if (namebuf == NULL || errmsg == NULL)
goto qf_init_end;
if ((fd = fopen((char *)efile, "r")) == NULL)
{
emsg2(e_openerrf, efile);
goto qf_init_end;
}
/*
* When the stack is full, remove to oldest entry
* Otherwise, add a new entry.
*/
if (qf_listcount == LISTCOUNT)
{
qf_free(0);
for (i = 1; i < LISTCOUNT; ++i)
qf_lists[i - 1] = qf_lists[i];
qf_curlist = LISTCOUNT - 1;
}
else
qf_curlist = qf_listcount++;
qf_lists[qf_curlist].qf_index = 0;
qf_lists[qf_curlist].qf_count = 0;
/*
* Each part of the format string is copied and modified from errorformat to
* fmtstr. Only a few % characters are allowed.
*/
efm = errorformat;
while (efm[0])
{
/*
* Allocate a new eformat structure and put it at the end of the list
*/
fmt_ptr = (struct eformat *)alloc((unsigned)sizeof(struct eformat));
if (fmt_ptr == NULL)
goto error2;
if (fmt_first == NULL) /* first one */
fmt_first = fmt_ptr;
else
fmt_last->next = fmt_ptr;
fmt_last = fmt_ptr;
fmt_ptr->next = NULL;
fmt_ptr->adr_cnt = 0;
/*
* Isolate one part in the 'errorformat' option
*/
for (len = 0; efm[len] != NUL && efm[len] != ','; ++len)
if (efm[len] == '\\' && efm[len + 1] != NUL)
++len;
/*
* Get some space to modify the format string into.
* Must be able to do the largest expansion (x3) MAX_ADDR times.
*/
maxlen = len + MAX_ADDR * 3 + 4;
if ((fmt_ptr->fmtstr = alloc(maxlen)) == NULL)
goto error2;
for (i = 0; i < MAX_ADDR; ++i)
fmt_ptr->adr[i] = NULL;
for (efmp = efm, i = 0; efmp < efm + len; ++efmp, ++i)
{
if (efmp[0] != '%') /* copy normal character */
{
if (efmp[0] == '\\' && efmp + 1 < efm + len)
++efmp;
fmt_ptr->fmtstr[i] = efmp[0];
}
else
{
fmt_ptr->fmtstr[i++] = '%';
switch (efmp[1])
{
case 'f': /* file name */
fmt_ptr->adr[fmt_ptr->adr_cnt++] = namebuf;
/* FALLTHROUGH */
case 'm': /* message */
if (efmp[1] == 'm')
fmt_ptr->adr[fmt_ptr->adr_cnt++] = errmsg;
fmt_ptr->fmtstr[i++] = '[';
fmt_ptr->fmtstr[i++] = '^';
#ifdef __EMX__
/* don't allow spaces in file name. This fixes
* the broken sscanf() where an empty message
* is accepted as a valid conversion.
*/
if (efmp[1] == 'f')
fmt_ptr->fmtstr[i++] = ' ';
#endif
if (efmp[2] == '\\') /* could be "%m\," */
j = 3;
else
j = 2;
if (efmp + j < efm + len)
fmt_ptr->fmtstr[i++] = efmp[j];
else
{
/*
* The %f or %m is the last one in the format,
* stop at the CR of NL at the end of the line.
*/
#ifdef USE_CRNL
fmt_ptr->fmtstr[i++] = '\r';
#endif
fmt_ptr->fmtstr[i++] = '\n';
}
fmt_ptr->fmtstr[i] = ']';
break;
case 'c': /* column */
fmt_ptr->adr[fmt_ptr->adr_cnt++] = &col;
fmt_ptr->fmtstr[i] = 'd';
break;
case 'l': /* line */
fmt_ptr->adr[fmt_ptr->adr_cnt++] = &lnum;
fmt_ptr->fmtstr[i++] = 'l';
fmt_ptr->fmtstr[i] = 'd';
break;
case 'n': /* error number */
fmt_ptr->adr[fmt_ptr->adr_cnt++] = &enr;
fmt_ptr->fmtstr[i] = 'd';
break;
case 't': /* error type */
fmt_ptr->adr[fmt_ptr->adr_cnt++] = &type;
fmt_ptr->fmtstr[i] = 'c';
break;
case '%': /* %% */
case '*': /* %*: no assignment */
fmt_ptr->fmtstr[i] = efmp[1];
break;
default:
EMSG("invalid % in format string");
goto error2;
}
if (fmt_ptr->adr_cnt == MAX_ADDR)
{
EMSG("too many % in format string");
goto error2;
}
++efmp;
}
if (i >= maxlen - 6)
{
EMSG("invalid format string");
goto error2;
}
}
fmt_ptr->fmtstr[i] = NUL;
/*
* Advance to next part
*/
efm = skip_to_option_part(efm + len); /* skip comma and spaces */
}
if (fmt_first == NULL) /* nothing found */
{
EMSG("'errorformat' contains no pattern");
goto error2;
}
/*
* got_int is reset here, because it was probably set when killing the
* ":make" command, but we still want to read the errorfile then.
*/
got_int = FALSE;
/*
* Read the lines in the error file one by one.
* Try to recognize one of the error formats in each line.
*/
while (fgets((char *)IObuff, CMDBUFFSIZE, fd) != NULL && !got_int)
{
if ((qfp = (struct qf_line *)alloc((unsigned)sizeof(struct qf_line)))
== NULL)
goto error2;
IObuff[CMDBUFFSIZE] = NUL; /* for very long lines */
/*
* Try to match each part of 'errorformat' until we find a complete
* match or none matches.
*/
valid = TRUE;
for (fmt_ptr = fmt_first; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next)
{
namebuf[0] = NUL;
errmsg[0] = NUL;
lnum = 0;
col = 0;
enr = -1;
type = 0;
/*
* If first char of the format and message don't match, there is
* no need to try sscanf() on it... Somehow I believe there are
* very slow implementations of sscanf().
* -- Paul Slootman
*/
if (fmt_ptr->fmtstr[0] != '%' && fmt_ptr->fmtstr[0] != IObuff[0])
continue;
if (sscanf((char *)IObuff, (char *)fmt_ptr->fmtstr,
fmt_ptr->adr[0], fmt_ptr->adr[1], fmt_ptr->adr[2],
fmt_ptr->adr[3], fmt_ptr->adr[4], fmt_ptr->adr[5],
fmt_ptr->adr[6]) == fmt_ptr->adr_cnt)
break;
}
if (fmt_ptr == NULL)
{
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 ((efmp = vim_strrchr(errmsg, '\n')) != NULL)
*efmp = NUL;
#ifdef USE_CRNL
if ((efmp = vim_strrchr(errmsg, '\r')) != NULL)
*efmp = NUL;
#endif
}
if (namebuf[0] == NUL) /* no file name */
qfp->qf_fnum = 0;
else
#ifdef RISCOS
/* Name is reported as `main.c', but file is `c.main' */
qfp->qf_fnum = ro_buflist_add(namebuf);
#else
qfp->qf_fnum = buflist_add(namebuf);
#endif
if ((qfp->qf_text = vim_strsave(errmsg)) == NULL)
goto error1;
if (!vim_isprintc(type)) /* only printable chars allowed */
type = 0;
qfp->qf_lnum = lnum;
qfp->qf_col = col;
qfp->qf_nr = enr;
qfp->qf_type = type;
qfp->qf_valid = valid;
if (qf_lists[qf_curlist].qf_count == 0) /* first element in the list */
{
qf_lists[qf_curlist].qf_start = qfp;
qfp->qf_prev = qfp; /* first element points to itself */
}
else
{
qfp->qf_prev = qfprev;
qfprev->qf_next = qfp;
}
qfp->qf_next = qfp; /* last element points to itself */
qfp->qf_cleared = FALSE;
qfprev = qfp;
++qf_lists[qf_curlist].qf_count;
if (qf_lists[qf_curlist].qf_index == 0 && qfp->qf_valid) /* first valid entry */
{
qf_lists[qf_curlist].qf_index = qf_lists[qf_curlist].qf_count;
qf_lists[qf_curlist].qf_ptr = qfp;
}
line_breakcheck();
}
if (!ferror(fd))
{
if (qf_lists[qf_curlist].qf_index == 0) /* no valid entry found */
{
qf_lists[qf_curlist].qf_ptr = qf_lists[qf_curlist].qf_start;
qf_lists[qf_curlist].qf_index = 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -