aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMarco Hinz <mh.codebro@gmail.com>2019-11-04 12:28:31 +0100
committerGitHub <noreply@github.com>2019-11-04 12:28:31 +0100
commit610755ff62049bd38cc72524a26ac0a27d951c3c (patch)
treef7e208bc02d8d7aa97e5d0bb32cf03b954efd41c /src
parente2b54fda7282c9237952b769afd2a313564cba79 (diff)
parent98d7c7db1269bdbc210beb0b3d08259f255252fb (diff)
downloadrneovim-610755ff62049bd38cc72524a26ac0a27d951c3c.tar.gz
rneovim-610755ff62049bd38cc72524a26ac0a27d951c3c.tar.bz2
rneovim-610755ff62049bd38cc72524a26ac0a27d951c3c.zip
Merge PR #11291 from shanesmith/vim-8.1.1256-take2
Diffstat (limited to 'src')
-rw-r--r--src/nvim/ex_cmds.lua24
-rw-r--r--src/nvim/memory.c2
-rw-r--r--src/nvim/quickfix.c2457
-rw-r--r--src/nvim/testdir/test_quickfix.vim143
4 files changed, 1713 insertions, 913 deletions
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 6317ec77ff..f7aa8a994a 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -323,6 +323,12 @@ return {
func='ex_abclear',
},
{
+ command='cabove',
+ flags=bit.bor(RANGE, TRLBAR),
+ addr_type=ADDR_OTHER ,
+ func='ex_cbelow',
+ },
+ {
command='caddbuffer',
flags=bit.bor(RANGE, NOTADR, WORD1, TRLBAR),
addr_type=ADDR_LINES,
@@ -359,6 +365,12 @@ return {
func='ex_cbuffer',
},
{
+ command='cbelow',
+ flags=bit.bor(RANGE, TRLBAR),
+ addr_type=ADDR_OTHER ,
+ func='ex_cbelow',
+ },
+ {
command='cbottom',
flags=bit.bor(TRLBAR),
addr_type=ADDR_LINES,
@@ -1273,6 +1285,12 @@ return {
func='ex_last',
},
{
+ command='labove',
+ flags=bit.bor(RANGE, TRLBAR),
+ addr_type=ADDR_OTHER ,
+ func='ex_cbelow',
+ },
+ {
command='language',
flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
addr_type=ADDR_LINES,
@@ -1309,6 +1327,12 @@ return {
func='ex_cbuffer',
},
{
+ command='lbelow',
+ flags=bit.bor(RANGE, TRLBAR),
+ addr_type=ADDR_OTHER ,
+ func='ex_cbelow',
+ },
+ {
command='lbottom',
flags=bit.bor(TRLBAR),
addr_type=ADDR_LINES,
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 64aae71433..9bc6b23ce3 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -693,6 +693,8 @@ void free_all_mem(void)
clear_hl_tables(false);
list_free_log();
+
+ check_quickfix_busy();
}
#endif
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index a9e0c22a76..cf5194d16f 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -80,6 +80,15 @@ struct qfline_S {
#define LISTCOUNT 10
#define INVALID_QFIDX (-1)
+/// Quickfix list type.
+typedef enum
+{
+ QFLT_QUICKFIX, ///< Quickfix list - global list
+ QFLT_LOCATION, ///< Location list - per window list
+ QFLT_INTERNAL ///< Internal - Temporary list used by
+ // getqflist()/getloclist()
+} qfltype_T;
+
/// Quickfix/Location list definition
///
/// Usually the list contains one or more entries. But an empty list can be
@@ -87,6 +96,7 @@ struct qfline_S {
/// information and entries can be added later using setqflist()/setloclist().
typedef struct qf_list_S {
unsigned qf_id; ///< Unique identifier for this list
+ qfltype_T qfl_type;
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
@@ -120,6 +130,7 @@ struct qf_info_S {
int qf_listcount; /* current number of lists */
int qf_curlist; /* current error list */
qf_list_T qf_lists[LISTCOUNT];
+ qfltype_T qfl_type; // type of list
};
static qf_info_T ql_info; // global quickfix list
@@ -154,6 +165,13 @@ struct efm_S {
int conthere; /* %> used */
};
+/// List of location lists to be deleted.
+/// Used to delay the deletion of locations lists by autocmds.
+typedef struct qf_delq_S {
+ struct qf_delq_S *next;
+ qf_info_T *qi;
+} qf_delq_T;
+
enum {
QF_FAIL = 0,
QF_OK = 1,
@@ -163,6 +181,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;
@@ -196,14 +216,19 @@ typedef struct {
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "quickfix.c.generated.h"
#endif
-/* Quickfix window check helper macro */
+
+static char_u *e_no_more_items = (char_u *)N_("E553: No more items");
+
+// Quickfix window check helper macro
#define IS_QF_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref == NULL)
/* Location list window check helper macro */
#define IS_LL_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)
// Quickfix and location list stack check helper macros
-#define IS_QF_STACK(qi) (qi == &ql_info)
-#define IS_LL_STACK(qi) (qi != &ql_info)
+#define IS_QF_STACK(qi) (qi->qfl_type == QFLT_QUICKFIX)
+#define IS_LL_STACK(qi) (qi->qfl_type == QFLT_LOCATION)
+#define IS_QF_LIST(qfl) (qfl->qfl_type == QFLT_QUICKFIX)
+#define IS_LL_LIST(qfl) (qfl->qfl_type == QFLT_LOCATION)
//
// Return location list for window 'wp'
@@ -211,6 +236,14 @@ typedef struct {
//
#define GET_LOC_LIST(wp) (IS_LL_WINDOW(wp) ? wp->w_llist_ref : wp->w_llist)
+// Macro to loop through all the items in a quickfix list
+// Quickfix item index starts from 1, so i below starts at 1
+#define FOR_ALL_QFL_ITEMS(qfl, qfp, i) \
+ for (i = 1, qfp = qfl->qf_start; /* NOLINT(readability/braces) */ \
+ !got_int && i <= qfl->qf_count && qfp != NULL; \
+ i++, qfp = qfp->qf_next)
+
+
// Looking up a buffer can be slow if there are many. Remember the last one
// to make this a lot faster if there are multiple matches in the same file.
static char_u *qf_last_bufname = NULL;
@@ -218,6 +251,50 @@ static bufref_T qf_last_bufref = { NULL, 0, 0 };
static char *e_loc_list_changed = N_("E926: Current location list was changed");
+// Counter to prevent autocmds from freeing up location lists when they are
+// still being used.
+static int quickfix_busy = 0;
+static qf_delq_T *qf_delq_head = NULL;
+
+/// Process the next line from a file/buffer/list/string and add it
+/// to the quickfix list 'qfl'.
+static int qf_init_process_nextline(qf_list_T *qfl,
+ efm_T *fmt_first,
+ qfstate_T *state,
+ qffields_T *fields)
+{
+ int status;
+
+ // Get the next line from a file/buffer/list/string
+ status = qf_get_nextline(state);
+ if (status != QF_OK) {
+ return status;
+ }
+
+ status = qf_parse_line(qfl, state->linebuf, state->linelen,
+ fmt_first, fields);
+ if (status != QF_OK) {
+ return status;
+ }
+
+ return qf_add_entry(qfl,
+ qfl->qf_directory,
+ (*fields->namebuf || qfl->qf_directory != NULL)
+ ? fields->namebuf
+ : ((qfl->qf_currfile != NULL && fields->valid)
+ ? qfl->qf_currfile : (char_u *)NULL),
+ fields->module,
+ 0,
+ fields->errmsg,
+ fields->lnum,
+ fields->col,
+ fields->use_viscol,
+ fields->pattern,
+ fields->enr,
+ fields->type,
+ fields->valid);
+}
+
/// Read the errorfile "efile" into memory, line by line, building the error
/// list. Set the error list's title to qf_title.
///
@@ -265,93 +342,96 @@ static struct fmtpattern
{ 'o', ".\\+" }
};
-// 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,
+/// Convert an errorformat pattern to a regular expression pattern.
+/// 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(
+/// Convert a scanf like format in 'errorformat' to a regular expression.
+/// 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 +439,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 +453,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)
+/// Analyze/parse an errorformat prefix.
+static const 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 +496,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 +515,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 +541,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 +557,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 +604,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 +618,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 +646,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
@@ -784,32 +888,41 @@ static int qf_get_nextline(qfstate_T *state)
return QF_OK;
}
-// Returns true if the specified quickfix/location stack is empty
+/// Returns true if the specified quickfix/location stack is empty
static bool qf_stack_empty(const qf_info_T *qi)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return qi == NULL || qi->qf_listcount <= 0;
}
-// Returns true if the specified quickfix/location list is empty.
-static bool qf_list_empty(const qf_info_T *qi, int qf_idx)
+/// Returns true if the specified quickfix/location list is empty.
+static bool qf_list_empty(qf_list_T *qfl)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- if (qi == NULL || qf_idx < 0 || qf_idx >= LISTCOUNT) {
- return true;
- }
- return qi->qf_lists[qf_idx].qf_count <= 0;
+ return qfl == NULL || qfl->qf_count <= 0;
+}
+
+/// Returns true if the specified quickfix/location list is not empty and
+/// has valid entries.
+static bool qf_list_has_valid_entries(qf_list_T *qfl)
+{
+ return !qf_list_empty(qfl) && !qfl->qf_nonevalid;
+}
+
+/// Return a pointer to a list in the specified quickfix stack
+static qf_list_T * qf_get_list(qf_info_T *qi, int idx)
+{
+ return &qi->qf_lists[idx];
}
/// Parse a line and get the quickfix fields.
/// Return the QF_ status.
-static int qf_parse_line(qf_info_T *qi, int qf_idx, char_u *linebuf,
+static int qf_parse_line(qf_list_T *qfl, char_u *linebuf,
size_t linelen, efm_T *fmt_first, qffields_T *fields)
{
efm_T *fmt_ptr;
int idx = 0;
char_u *tail = NULL;
- qf_list_T *qfl = &qi->qf_lists[qf_idx];
int status;
restofline:
@@ -865,7 +978,7 @@ restofline:
qfl->qf_multiignore = false; // reset continuation
} else if (vim_strchr((char_u *)"CZ", idx) != NULL) {
// continuation of multi-line msg
- status = qf_parse_multiline_pfx(qi, qf_idx, idx, qfl, fields);
+ status = qf_parse_multiline_pfx(idx, qfl, fields);
if (status != QF_OK) {
return status;
}
@@ -1011,12 +1124,12 @@ 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(qf_get_list(qi, qf_idx) )) {
old_last = qi->qf_lists[qf_idx].qf_last;
}
}
- qf_list_T *qfl = &qi->qf_lists[qf_idx];
+ qf_list_T *qfl = qf_get_list(qi, qf_idx);
// Use the local value of 'errorformat' if it's set.
if (errorformat == p_efm && tv == NULL && buf && *buf->b_p_efm != NUL) {
@@ -1054,39 +1167,14 @@ qf_init_ext(
* Try to recognize one of the error formats in each line.
*/
while (!got_int) {
- // Get the next line from a file/buffer/list/string
- status = qf_get_nextline(&state);
+ status = qf_init_process_nextline(qfl, fmt_first, &state, &fields);
if (status == QF_END_OF_INPUT) { // end of input
break;
}
-
- status = qf_parse_line(qi, qf_idx, state.linebuf, state.linelen,
- fmt_first, &fields);
if (status == QF_FAIL) {
goto error2;
}
- if (status == QF_IGNORE_LINE) {
- continue;
- }
- if (qf_add_entry(qi,
- qf_idx,
- qfl->qf_directory,
- (*fields.namebuf || qfl->qf_directory)
- ? fields.namebuf : ((qfl->qf_currfile && fields.valid)
- ? qfl->qf_currfile : (char_u *)NULL),
- fields.module,
- 0,
- fields.errmsg,
- fields.lnum,
- fields.col,
- fields.use_viscol,
- fields.pattern,
- fields.enr,
- fields.type,
- fields.valid) == FAIL) {
- goto error2;
- }
line_breakcheck();
}
if (state.fd == NULL || !ferror(state.fd)) {
@@ -1109,7 +1197,7 @@ qf_init_ext(
error2:
if (!adding) {
// Error when creating a new list. Free the new list
- qf_free(qi, qi->qf_curlist);
+ qf_free(qfl);
qi->qf_listcount--;
if (qi->qf_curlist > 0) {
qi->qf_curlist--;
@@ -1127,16 +1215,16 @@ qf_init_end:
/// Set the title of the specified quickfix list. Frees the previous title.
/// Prepends ':' to the title.
-static void qf_store_title(qf_info_T *qi, int qf_idx, const char_u *title)
+static void qf_store_title(qf_list_T *qfl, const char_u *title)
FUNC_ATTR_NONNULL_ARG(1)
{
- XFREE_CLEAR(qi->qf_lists[qf_idx].qf_title);
+ XFREE_CLEAR(qfl->qf_title);
if (title != NULL) {
size_t len = STRLEN(title) + 1;
char_u *p = xmallocz(len);
- qi->qf_lists[qf_idx].qf_title = p;
+ qfl->qf_title = p;
xstrlcpy((char *)p, (const char *)title, len + 1);
}
}
@@ -1154,35 +1242,256 @@ static char_u * qf_cmdtitle(char_u *cmd)
return qftitle_str;
}
-// Prepare for adding a new quickfix list. If the current list is in the
-// middle of the stack, then all the following lists are freed and then
-// the new list is added.
+/// Return a pointer to the current list in the specified quickfix stack
+static qf_list_T * qf_get_curlist(qf_info_T *qi)
+{
+ return qf_get_list(qi, qi->qf_curlist);
+}
+
+/// Prepare for adding a new quickfix list. If the current list is in the
+/// middle of the stack, then all the following lists are freed and then
+/// the new list is added.
static void qf_new_list(qf_info_T *qi, const char_u *qf_title)
{
int i;
+ qf_list_T *qfl;
// If the current entry is not the last entry, delete entries beyond
// the current entry. This makes it possible to browse in a tree-like
// way with ":grep'.
- while (qi->qf_listcount > qi->qf_curlist + 1)
- qf_free(qi, --qi->qf_listcount);
+ while (qi->qf_listcount > qi->qf_curlist + 1) {
+ qf_free(&qi->qf_lists[--qi->qf_listcount]);
+ }
/*
* When the stack is full, remove to oldest entry
* Otherwise, add a new entry.
*/
if (qi->qf_listcount == LISTCOUNT) {
- qf_free(qi, 0);
- for (i = 1; i < LISTCOUNT; ++i)
+ qf_free(&qi->qf_lists[0]);
+ for (i = 1; i < LISTCOUNT; i++) {
qi->qf_lists[i - 1] = qi->qf_lists[i];
+ }
qi->qf_curlist = LISTCOUNT - 1;
} else
qi->qf_curlist = qi->qf_listcount++;
- memset(&qi->qf_lists[qi->qf_curlist], 0, (size_t)(sizeof(qf_list_T)));
- qf_store_title(qi, qi->qf_curlist, qf_title);
- qi->qf_lists[qi->qf_curlist].qf_id = ++last_qf_id;
+ qfl = qf_get_curlist(qi);
+ memset(qfl, 0, sizeof(qf_list_T));
+ qf_store_title(qfl, qf_title);
+ qfl->qfl_type = qi->qfl_type;
+ qfl->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 +1502,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 +1517,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;
- }
- 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;
+
+ if (status != QF_OK) {
+ return status;
}
- STRNCAT(fields->module, regmatch->startp[i], len);
}
return QF_OK;
@@ -1430,8 +1648,7 @@ static int qf_parse_line_nomatch(char_u *linebuf, size_t linelen,
}
/// Parse multi-line error format prefixes (%C and %Z)
-static int qf_parse_multiline_pfx(qf_info_T *qi, int qf_idx, int idx,
- qf_list_T *qfl, qffields_T *fields)
+static int qf_parse_multiline_pfx(int idx, qf_list_T *qfl, qffields_T *fields)
{
if (!qfl->qf_multiignore) {
qfline_T *qfprev = qfl->qf_last;
@@ -1462,7 +1679,7 @@ static int qf_parse_multiline_pfx(qf_info_T *qi, int qf_idx, int idx,
}
qfprev->qf_viscol = fields->use_viscol;
if (!qfprev->qf_fnum) {
- qfprev->qf_fnum = qf_get_fnum(qi, qf_idx, qfl->qf_directory,
+ qfprev->qf_fnum = qf_get_fnum(qfl, qfl->qf_directory,
*fields->namebuf || qfl->qf_directory
? fields->namebuf
: qfl->qf_currfile && fields->valid
@@ -1477,7 +1694,18 @@ static int qf_parse_multiline_pfx(qf_info_T *qi, int qf_idx, int idx,
return QF_IGNORE_LINE;
}
-/// Free a location list.
+/// Queue location list stack delete request.
+static void locstack_queue_delreq(qf_info_T *qi)
+{
+ qf_delq_T *q;
+
+ q = xmalloc(sizeof(qf_delq_T));
+ q->qi = qi;
+ q->next = qf_delq_head;
+ qf_delq_head = q;
+}
+
+/// Free a location list stack
static void ll_free_all(qf_info_T **pqi)
{
int i;
@@ -1490,10 +1718,17 @@ static void ll_free_all(qf_info_T **pqi)
qi->qf_refcount--;
if (qi->qf_refcount < 1) {
- /* No references to this location list */
- for (i = 0; i < qi->qf_listcount; ++i)
- qf_free(qi, i);
- xfree(qi);
+ // No references to this location list.
+ // If the location list is still in use, then queue the delete request
+ // to be processed later.
+ if (quickfix_busy > 0) {
+ locstack_queue_delreq(qi);
+ } else {
+ for (i = 0; i < qi->qf_listcount; i++) {
+ qf_free(qf_get_list(qi, i));
+ }
+ xfree(qi);
+ }
}
}
@@ -1507,16 +1742,61 @@ void qf_free_all(win_T *wp)
/* location list */
ll_free_all(&wp->w_llist);
ll_free_all(&wp->w_llist_ref);
- } else
- /* quickfix list */
- for (i = 0; i < qi->qf_listcount; ++i)
- qf_free(qi, i);
+ } else {
+ // quickfix list
+ for (i = 0; i < qi->qf_listcount; i++) {
+ qf_free(qf_get_list(qi, i));
+ }
+ }
+}
+
+/// Delay freeing of location list stacks when the quickfix code is running.
+/// Used to avoid problems with autocmds freeing location list stacks when the
+/// quickfix code is still referencing the stack.
+/// Must always call decr_quickfix_busy() exactly once after this.
+static void incr_quickfix_busy(void)
+{
+ quickfix_busy++;
}
+/// Safe to free location list stacks. Process any delayed delete requests.
+static void decr_quickfix_busy(void)
+{
+ quickfix_busy--;
+ if (quickfix_busy == 0) {
+ // No longer referencing the location lists. Process all the pending
+ // delete requests.
+ while (qf_delq_head != NULL) {
+ qf_delq_T *q = qf_delq_head;
+
+ qf_delq_head = q->next;
+ ll_free_all(&q->qi);
+ xfree(q);
+ }
+ }
+#ifdef ABORT_ON_INTERNAL_ERROR
+ if (quickfix_busy < 0) {
+ EMSG("quickfix_busy has become negative");
+ abort();
+ }
+#endif
+}
+
+#if defined(EXITFREE)
+void check_quickfix_busy(void)
+{
+ if (quickfix_busy != 0) {
+ EMSGN("quickfix_busy not zero on exit: %ld", (long)quickfix_busy);
+# ifdef ABORT_ON_INTERNAL_ERROR
+ abort();
+# endif
+ }
+}
+#endif
+
/// Add an entry to the end of the list of errors.
///
-/// @param qi quickfix list
-/// @param qf_idx list index
+/// @param qfl quickfix list entry
/// @param dir optional directory name
/// @param fname file name or NULL
/// @param module module name or NULL
@@ -1530,8 +1810,8 @@ void qf_free_all(win_T *wp)
/// @param type type character
/// @param valid valid entry
///
-/// @returns OK or FAIL.
-static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname,
+/// @returns QF_OK or QF_FAIL.
+static int qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname,
char_u *module, int bufnum, char_u *mesg, long lnum,
int col, char_u vis_col, char_u *pattern, int nr,
char_u type, char_u valid)
@@ -1545,10 +1825,10 @@ static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname,
qfp->qf_fnum = bufnum;
if (buf != NULL) {
buf->b_has_qf_entry |=
- IS_QF_STACK(qi) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
+ IS_QF_LIST(qfl) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
}
} else {
- qfp->qf_fnum = qf_get_fnum(qi, qf_idx, dir, fname);
+ qfp->qf_fnum = qf_get_fnum(qfl, dir, fname);
}
qfp->qf_text = vim_strsave(mesg);
qfp->qf_lnum = lnum;
@@ -1571,12 +1851,12 @@ static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname,
qfp->qf_type = (char_u)type;
qfp->qf_valid = valid;
- lastp = &qi->qf_lists[qf_idx].qf_last;
- if (qi->qf_lists[qf_idx].qf_count == 0) {
+ lastp = &qfl->qf_last;
+ if (qf_list_empty(qfl)) {
// first element in the list
- qi->qf_lists[qf_idx].qf_start = qfp;
- qi->qf_lists[qf_idx].qf_ptr = qfp;
- qi->qf_lists[qf_idx].qf_index = 0;
+ qfl->qf_start = qfp;
+ qfl->qf_ptr = qfp;
+ qfl->qf_index = 0;
qfp->qf_prev = NULL;
} else {
assert(*lastp);
@@ -1586,32 +1866,29 @@ static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname,
qfp->qf_next = NULL;
qfp->qf_cleared = false;
*lastp = qfp;
- qi->qf_lists[qf_idx].qf_count++;
- if (qi->qf_lists[qf_idx].qf_index == 0 && qfp->qf_valid) {
+ qfl->qf_count++;
+ if (qfl->qf_index == 0 && qfp->qf_valid) {
// first valid entry
- qi->qf_lists[qf_idx].qf_index = qi->qf_lists[qf_idx].qf_count;
- qi->qf_lists[qf_idx].qf_ptr = qfp;
+ qfl->qf_index = qfl->qf_count;
+ qfl->qf_ptr = qfp;
}
- return OK;
+ return QF_OK;
}
-/*
- * Allocate a new location list
- */
-static qf_info_T *ll_new_list(void)
+/// Allocate a new quickfix/location list stack
+static qf_info_T *qf_alloc_stack(qfltype_T qfltype)
FUNC_ATTR_NONNULL_RET
{
qf_info_T *qi = xcalloc(1, sizeof(qf_info_T));
qi->qf_refcount++;
+ qi->qfl_type = qfltype;
return qi;
}
-/*
- * Return the location list for window 'wp'.
- * If not present, allocate a location list
- */
+/// Return the location list stack for window 'wp'.
+/// If not present, allocate a location list stack
static qf_info_T *ll_get_or_alloc_list(win_T *wp)
{
if (IS_LL_WINDOW(wp))
@@ -1624,26 +1901,63 @@ static qf_info_T *ll_get_or_alloc_list(win_T *wp)
*/
ll_free_all(&wp->w_llist_ref);
- if (wp->w_llist == NULL)
- wp->w_llist = ll_new_list(); /* new location list */
+ if (wp->w_llist == NULL) {
+ wp->w_llist = qf_alloc_stack(QFLT_LOCATION); // new location list
+ }
return wp->w_llist;
}
-// Copy location list entries from 'from_qfl' to 'to_qfl'.
-static int copy_loclist_entries(const qf_list_T *from_qfl,
- qf_list_T *to_qfl,
- qf_info_T *to_qi)
+/// Get the quickfix/location list stack to use for the specified Ex command.
+/// For a location list command, returns the stack for the current window. If
+/// the location list is not found, then returns NULL and prints an error
+/// message if 'print_emsg' is TRUE.
+static qf_info_T * qf_cmd_get_stack(exarg_T *eap, int print_emsg)
+{
+ qf_info_T *qi = &ql_info;
+
+ if (is_loclist_cmd(eap->cmdidx)) {
+ qi = GET_LOC_LIST(curwin);
+ if (qi == NULL) {
+ if (print_emsg) {
+ EMSG(_(e_loclist));
+ }
+ return NULL;
+ }
+ }
+
+ return qi;
+}
+
+/// Get the quickfix/location list stack to use for the specified Ex command.
+/// For a location list command, returns the stack for the current window.
+/// If the location list is not present, then allocates a new one.
+/// Returns NULL if the allocation fails. For a location list command, sets
+/// 'pwinp' to curwin.
+static qf_info_T * qf_cmd_get_or_alloc_stack(exarg_T *eap, win_T **pwinp)
+{
+ qf_info_T *qi = &ql_info;
+
+ if (is_loclist_cmd(eap->cmdidx)) {
+ qi = ll_get_or_alloc_list(curwin);
+ if (qi == NULL) {
+ return NULL;
+ }
+ *pwinp = curwin;
+ }
+
+ return qi;
+}
+
+/// Copy location list entries from 'from_qfl' to 'to_qfl'.
+static int copy_loclist_entries(const qf_list_T *from_qfl, qf_list_T *to_qfl)
FUNC_ATTR_NONNULL_ALL
{
int i;
- const qfline_T *from_qfp;
+ qfline_T *from_qfp;
// copy all the location entries in this list
- 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_qi,
- to_qi->qf_curlist,
+ FOR_ALL_QFL_ITEMS(from_qfl, from_qfp, i) {
+ if (qf_add_entry(to_qfl,
NULL,
NULL,
from_qfp->qf_module,
@@ -1655,7 +1969,7 @@ static int copy_loclist_entries(const qf_list_T *from_qfl,
from_qfp->qf_pattern,
from_qfp->qf_nr,
0,
- from_qfp->qf_valid) == FAIL) {
+ from_qfp->qf_valid) == QF_FAIL) {
return FAIL;
}
@@ -1673,13 +1987,12 @@ static int copy_loclist_entries(const qf_list_T *from_qfl,
return OK;
}
-// Copy the specified location list 'from_qfl' to 'to_qfl'.
-static int copy_loclist(const qf_list_T *from_qfl,
- qf_list_T *to_qfl,
- qf_info_T *to_qi)
+/// Copy the specified location list 'from_qfl' to 'to_qfl'.
+static int copy_loclist(const qf_list_T *from_qfl, qf_list_T *to_qfl)
FUNC_ATTR_NONNULL_ALL
{
// Some of the fields are populated by qf_add_entry()
+ to_qfl->qfl_type = from_qfl->qfl_type;
to_qfl->qf_nonevalid = from_qfl->qf_nonevalid;
to_qfl->qf_count = 0;
to_qfl->qf_index = 0;
@@ -1697,8 +2010,9 @@ static int copy_loclist(const qf_list_T *from_qfl,
} else {
to_qfl->qf_ctx = NULL;
}
+
if (from_qfl->qf_count) {
- if (copy_loclist_entries(from_qfl, to_qfl, to_qi) == FAIL) {
+ if (copy_loclist_entries(from_qfl, to_qfl) == FAIL) {
return FAIL;
}
}
@@ -1739,7 +2053,7 @@ void copy_loclist_stack(win_T *from, win_T *to)
}
// allocate a new location list
- to->w_llist = ll_new_list();
+ to->w_llist = qf_alloc_stack(QFLT_LOCATION);
to->w_llist->qf_listcount = qi->qf_listcount;
@@ -1747,8 +2061,8 @@ void copy_loclist_stack(win_T *from, win_T *to)
for (int idx = 0; idx < qi->qf_listcount; idx++) {
to->w_llist->qf_curlist = idx;
- if (copy_loclist(&qi->qf_lists[idx], &to->w_llist->qf_lists[idx],
- to->w_llist) == FAIL) {
+ if (copy_loclist(qf_get_list(qi, idx),
+ qf_get_list(to->w_llist, idx)) == FAIL) {
qf_free_all(to);
return;
}
@@ -1757,10 +2071,9 @@ void copy_loclist_stack(win_T *from, win_T *to)
to->w_llist->qf_curlist = qi->qf_curlist; // current list
}
-// Get buffer number for file "directory/fname".
-// Also sets the b_has_qf_entry flag.
-static int qf_get_fnum(qf_info_T *qi, int qf_idx, char_u *directory,
- char_u *fname)
+/// Get buffer number for file "directory/fname".
+/// Also sets the b_has_qf_entry flag.
+static int qf_get_fnum(qf_list_T *qfl, char_u *directory, char_u *fname )
{
char_u *ptr = NULL;
char_u *bufname;
@@ -1783,7 +2096,7 @@ static int qf_get_fnum(qf_info_T *qi, int qf_idx, char_u *directory,
// directory change.
if (!os_path_exists(ptr)) {
xfree(ptr);
- directory = qf_guess_filepath(qi, qf_idx, fname);
+ directory = qf_guess_filepath(qfl, fname);
if (directory) {
ptr = (char_u *)concat_fnames((char *)directory, (char *)fname, true);
} else {
@@ -1811,7 +2124,7 @@ static int qf_get_fnum(qf_info_T *qi, int qf_idx, char_u *directory,
return 0;
}
buf->b_has_qf_entry =
- IS_QF_STACK(qi) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
+ IS_QF_LIST(qfl) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
return buf->b_fnum;
}
@@ -1913,30 +2226,29 @@ static void qf_clean_dir_stack(struct dir_stack_T **stackptr)
}
}
-// Check in which directory of the directory stack the given file can be
-// found.
-// Returns a pointer to the directory name or NULL if not found.
-// Cleans up intermediate directory entries.
-//
-// TODO(vim): How to solve the following problem?
-// If we have this directory tree:
-// ./
-// ./aa
-// ./aa/bb
-// ./bb
-// ./bb/x.c
-// and make says:
-// making all in aa
-// making all in bb
-// x.c:9: Error
-// Then qf_push_dir thinks we are in ./aa/bb, but we are in ./bb.
-// qf_guess_filepath will return NULL.
-static char_u *qf_guess_filepath(qf_info_T *qi, int qf_idx, char_u *filename)
+/// Check in which directory of the directory stack the given file can be
+/// found.
+/// Returns a pointer to the directory name or NULL if not found.
+/// Cleans up intermediate directory entries.
+///
+/// TODO(vim): How to solve the following problem?
+/// If we have this directory tree:
+/// ./
+/// ./aa
+/// ./aa/bb
+/// ./bb
+/// ./bb/x.c
+/// and make says:
+/// making all in aa
+/// making all in bb
+/// x.c:9: Error
+/// Then qf_push_dir thinks we are in ./aa/bb, but we are in ./bb.
+/// qf_guess_filepath will return NULL.
+static char_u *qf_guess_filepath(qf_list_T *qfl, char_u *filename)
{
struct dir_stack_T *ds_ptr;
struct dir_stack_T *ds_tmp;
char_u *fullname;
- qf_list_T *qfl = &qi->qf_lists[qf_idx];
// no dirs on the stack - there's nothing we can do
if (qfl->qf_dir_stack == NULL) {
@@ -1994,22 +2306,19 @@ static bool qflist_valid(win_T *wp, unsigned int qf_id)
/// This may invalidate the current quickfix entry. This function checks
/// whether an entry is still present in the quickfix list.
/// Similar to location list.
-static bool is_qf_entry_present(qf_info_T *qi, qfline_T *qf_ptr)
+static bool is_qf_entry_present(qf_list_T *qfl, qfline_T *qf_ptr)
{
- qf_list_T *qfl;
qfline_T *qfp;
int i;
- qfl = &qi->qf_lists[qi->qf_curlist];
-
// 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 == NULL || qfp == qf_ptr) {
+ FOR_ALL_QFL_ITEMS(qfl, qfp, i) {
+ if (qfp == qf_ptr) {
break;
}
}
- if (i == qfl->qf_count) { // Entry is not found
+ if (i > qfl->qf_count) { // Entry is not found
return false;
}
@@ -2018,20 +2327,19 @@ static bool is_qf_entry_present(qf_info_T *qi, qfline_T *qf_ptr)
/// Get the next valid entry in the current quickfix/location list. The search
/// starts from the current entry. Returns NULL on failure.
-static qfline_T *get_next_valid_entry(qf_info_T *qi, qfline_T *qf_ptr,
+static qfline_T *get_next_valid_entry(qf_list_T *qfl, qfline_T *qf_ptr,
int *qf_index, int dir)
{
int idx = *qf_index;
int old_qf_fnum = qf_ptr->qf_fnum;
do {
- if (idx == qi->qf_lists[qi->qf_curlist].qf_count
- || qf_ptr->qf_next == NULL) {
+ if (idx == qfl->qf_count || qf_ptr->qf_next == NULL) {
return NULL;
}
idx++;
qf_ptr = qf_ptr->qf_next;
- } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid && !qf_ptr->qf_valid)
+ } while ((!qfl->qf_nonevalid && !qf_ptr->qf_valid)
|| (dir == FORWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
*qf_index = idx;
@@ -2040,7 +2348,7 @@ static qfline_T *get_next_valid_entry(qf_info_T *qi, qfline_T *qf_ptr,
/// Get the previous valid entry in the current quickfix/location list. The
/// search starts from the current entry. Returns NULL on failure.
-static qfline_T *get_prev_valid_entry(qf_info_T *qi, qfline_T *qf_ptr,
+static qfline_T *get_prev_valid_entry(qf_list_T *qfl, qfline_T *qf_ptr,
int *qf_index, int dir)
{
int idx = *qf_index;
@@ -2052,7 +2360,7 @@ static qfline_T *get_prev_valid_entry(qf_info_T *qi, qfline_T *qf_ptr,
}
idx--;
qf_ptr = qf_ptr->qf_prev;
- } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid && !qf_ptr->qf_valid)
+ } while ((!qfl->qf_nonevalid && !qf_ptr->qf_valid)
|| (dir == BACKWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
*qf_index = idx;
@@ -2063,12 +2371,11 @@ static qfline_T *get_prev_valid_entry(qf_info_T *qi, qfline_T *qf_ptr,
/// the quickfix list.
/// dir == FORWARD or FORWARD_FILE: next valid entry
/// dir == BACKWARD or BACKWARD_FILE: previous valid entry
-static qfline_T *get_nth_valid_entry(qf_info_T *qi, int errornr,
+static qfline_T *get_nth_valid_entry(qf_list_T *qfl, int errornr,
qfline_T *qf_ptr, int *qf_index, int dir)
{
qfline_T *prev_qf_ptr;
int prev_index;
- static char_u *e_no_more_items = (char_u *)N_("E553: No more items");
char_u *err = e_no_more_items;
while (errornr--) {
@@ -2076,9 +2383,9 @@ static qfline_T *get_nth_valid_entry(qf_info_T *qi, int errornr,
prev_index = *qf_index;
if (dir == FORWARD || dir == FORWARD_FILE) {
- qf_ptr = get_next_valid_entry(qi, qf_ptr, qf_index, dir);
+ qf_ptr = get_next_valid_entry(qfl, qf_ptr, qf_index, dir);
} else {
- qf_ptr = get_prev_valid_entry(qi, qf_ptr, qf_index, dir);
+ qf_ptr = get_prev_valid_entry(qfl, qf_ptr, qf_index, dir);
}
if (qf_ptr == NULL) {
@@ -2098,7 +2405,7 @@ static qfline_T *get_nth_valid_entry(qf_info_T *qi, int errornr,
}
/// Get n'th (errornr) quickfix entry
-static qfline_T *get_nth_entry(qf_info_T *qi, int errornr, qfline_T *qf_ptr,
+static qfline_T *get_nth_entry(qf_list_T *qfl, int errornr, qfline_T *qf_ptr,
int *cur_qfidx)
{
int qf_idx = *cur_qfidx;
@@ -2111,7 +2418,7 @@ static qfline_T *get_nth_entry(qf_info_T *qi, int errornr, qfline_T *qf_ptr,
// New error number is greater than the current error number
while (errornr > qf_idx
- && qf_idx < qi->qf_lists[qi->qf_curlist].qf_count
+ && qf_idx < qfl->qf_count
&& qf_ptr->qf_next != NULL) {
qf_idx++;
qf_ptr = qf_ptr->qf_next;
@@ -2133,6 +2440,13 @@ static win_T *qf_find_help_win(void)
return NULL;
}
+/// Set the location list for the specified window to 'qi'.
+static void win_set_loclist(win_T *wp, qf_info_T *qi)
+{
+ wp->w_llist = qi;
+ qi->qf_refcount++;
+}
+
/// Find a help window or open one.
static int jump_to_help_window(qf_info_T *qi, int *opened_window)
{
@@ -2172,8 +2486,7 @@ static int jump_to_help_window(qf_info_T *qi, int *opened_window)
if (IS_LL_STACK(qi)) { // not a quickfix list
// The new window should use the supplied location list
- curwin->w_llist = qi;
- qi->qf_refcount++;
+ win_set_loclist(curwin, qi);
}
}
@@ -2209,7 +2522,7 @@ static win_T *qf_find_win_with_normal_buf(void)
return NULL;
}
-// Go to a window in any tabpage containing the specified file. Returns TRUE
+// Go to a window in any tabpage containing the specified file. Returns true
// if successfully jumped to the window. Otherwise returns FALSE.
static bool qf_goto_tabwin_with_file(int fnum)
{
@@ -2239,8 +2552,7 @@ static int qf_open_new_file_win(qf_info_T *ll_ref)
if (ll_ref != NULL) {
// The new window should use the location list from the
// location list window
- curwin->w_llist = ll_ref;
- ll_ref->qf_refcount++;
+ win_set_loclist(curwin, ll_ref);
}
return OK;
}
@@ -2281,9 +2593,10 @@ static void qf_goto_win_with_ll_file(win_T *use_win, int qf_fnum,
// If the location list for the window is not set, then set it
// to the location list from the location window
- if (win->w_llist == NULL) {
- win->w_llist = ll_ref;
- ll_ref->qf_refcount++;
+ if (win->w_llist == NULL && ll_ref != NULL) {
+ // The new window should use the location list from the
+ // location list window
+ win_set_loclist(win, ll_ref);
}
}
@@ -2382,6 +2695,8 @@ static int qf_jump_to_usable_window(int qf_fnum, int *opened_window)
static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit,
win_T *oldwin, int *opened_window, int *abort)
{
+ qf_list_T *qfl = qf_get_curlist(qi);
+ qfltype_T qfl_type = qfl->qfl_type;
int retval = OK;
if (qf_ptr->qf_type == 1) {
@@ -2396,12 +2711,12 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit,
oldwin == curwin ? curwin : NULL);
}
} else {
- unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
+ unsigned save_qfid = qfl->qf_id;
retval = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1,
GETF_SETMARK | GETF_SWITCH, forceit);
- if (IS_LL_STACK(qi)) {
+ if (qfl_type == QFLT_LOCATION) {
// Location list. Check whether the associated window is still
// present and the list is still valid.
if (!win_valid_any_tab(oldwin)) {
@@ -2412,8 +2727,8 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit,
EMSG(_(e_loc_list_changed));
*abort = true;
}
- } else if (!is_qf_entry_present(qi, qf_ptr)) {
- if (IS_QF_STACK(qi)) {
+ } else if (!is_qf_entry_present(qfl, qf_ptr)) {
+ if (qfl_type == QFLT_QUICKFIX) {
EMSG(_("E925: Current quickfix was changed"));
} else {
EMSG(_(e_loc_list_changed));
@@ -2477,7 +2792,7 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr,
update_topline_redraw();
}
snprintf((char *)IObuff, IOSIZE, _("(%d of %d)%s%s: "), qf_index,
- qi->qf_lists[qi->qf_curlist].qf_count,
+ qf_get_curlist(qi)->qf_count,
qf_ptr->qf_cleared ? _(" (line deleted)") : "",
(char *)qf_types(qf_ptr->qf_type, qf_ptr->qf_nr));
// Add the message, skipping leading whitespace and newlines.
@@ -2507,6 +2822,7 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr,
/// else go to entry "errornr"
void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit)
{
+ qf_list_T *qfl;
qfline_T *qf_ptr;
qfline_T *old_qf_ptr;
int qf_index;
@@ -2524,31 +2840,34 @@ void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit)
if (qi == NULL)
qi = &ql_info;
- if (qf_stack_empty(qi) || qf_list_empty(qi, qi->qf_curlist)) {
+ if (qf_stack_empty(qi) || qf_list_empty(qf_get_curlist(qi))) {
EMSG(_(e_quickfix));
return;
}
- qf_ptr = qi->qf_lists[qi->qf_curlist].qf_ptr;
+ qfl = qf_get_curlist(qi);
+
+ qf_ptr = qfl->qf_ptr;
old_qf_ptr = qf_ptr;
- qf_index = qi->qf_lists[qi->qf_curlist].qf_index;
+ qf_index = qfl->qf_index;
old_qf_index = qf_index;
if (dir != 0) { // next/prev valid entry
- qf_ptr = get_nth_valid_entry(qi, errornr, qf_ptr, &qf_index, dir);
+ qf_ptr = get_nth_valid_entry(qfl, errornr, qf_ptr, &qf_index, dir);
if (qf_ptr == NULL) {
qf_ptr = old_qf_ptr;
qf_index = old_qf_index;
goto theend; // The horror... the horror...
}
} else if (errornr != 0) { // go to specified number
- qf_ptr = get_nth_entry(qi, errornr, qf_ptr, &qf_index);
+ qf_ptr = get_nth_entry(qfl, errornr, qf_ptr, &qf_index);
}
- qi->qf_lists[qi->qf_curlist].qf_index = qf_index;
- if (qf_win_pos_update(qi, old_qf_index))
- /* No need to print the error message if it's visible in the error
- * window */
- print_message = FALSE;
+ qfl->qf_index = qf_index;
+ if (qf_win_pos_update(qi, old_qf_index)) {
+ // No need to print the error message if it's visible in the error
+ // window
+ print_message = false;
+ }
// For ":helpgrep" find a help window or open one.
if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer) || cmdmod.tab != 0)) {
@@ -2617,8 +2936,8 @@ failed:
}
theend:
if (qi != NULL) {
- qi->qf_lists[qi->qf_curlist].qf_ptr = qf_ptr;
- qi->qf_lists[qi->qf_curlist].qf_index = qf_index;
+ qfl->qf_ptr = qf_ptr;
+ qfl->qf_index = qf_index;
}
if (p_swb != old_swb && opened_window) {
/* Restore old 'switchbuf' value, but not when an autocommand or
@@ -2631,35 +2950,117 @@ 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.
+/// 'cursel' will be set to true for the currently selected entry in the
+/// quickfix list.
+static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel)
+{
+ 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, cursel ? 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;
+ qf_list_T *qfl;
+ 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;
+ qf_info_T *qi;
- if (is_loclist_cmd(eap->cmdidx)) {
- qi = GET_LOC_LIST(curwin);
- if (qi == NULL) {
- EMSG(_(e_loclist));
- return;
- }
+ if ((qi = qf_cmd_get_stack(eap, true)) == NULL) {
+ return;
}
- if (qf_stack_empty(qi) || qf_list_empty(qi, qi->qf_curlist)) {
+ if (qf_stack_empty(qi) || qf_list_empty(qf_get_curlist(qi))) {
EMSG(_(e_quickfix));
return;
}
@@ -2673,12 +3074,13 @@ void qf_list(exarg_T *eap)
EMSG(_(e_trailing));
return;
}
+ qfl = qf_get_curlist(qi);
if (plus) {
- i = qi->qf_lists[qi->qf_curlist].qf_index;
+ i = qfl->qf_index;
idx2 = i + idx1;
idx1 = i;
} else {
- i = qi->qf_lists[qi->qf_curlist].qf_count;
+ i = qfl->qf_count;
if (idx1 < 0) {
idx1 = (-idx1 > i) ? 0 : idx1 + i + 1;
}
@@ -2705,95 +3107,13 @@ void qf_list(exarg_T *eap)
qfLineAttr = HL_ATTR(HLF_N);
}
- if (qi->qf_lists[qi->qf_curlist].qf_nonevalid) {
+ if (qfl->qf_nonevalid) {
all = true;
}
- qfp = qi->qf_lists[qi->qf_curlist].qf_start;
- for (i = 1; !got_int && i <= qi->qf_lists[qi->qf_curlist].qf_count; ) {
+ FOR_ALL_QFL_ITEMS(qfl, qfp, i) {
if ((qfp->qf_valid || all) && idx1 <= i && i <= idx2) {
- if (got_int) {
- 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 */
- }
-
-next_entry:
- qfp = qfp->qf_next;
- if (qfp == NULL) {
- break;
+ qf_list_entry(qfp, i, i == qfl->qf_index);
}
- i++;
os_breakcheck();
}
}
@@ -2848,23 +3168,17 @@ static void qf_msg(qf_info_T *qi, int which, char *lead)
msg(buf);
}
-/*
- * ":colder [count]": Up in the quickfix stack.
- * ":cnewer [count]": Down in the quickfix stack.
- * ":lolder [count]": Up in the location list stack.
- * ":lnewer [count]": Down in the location list stack.
- */
+/// ":colder [count]": Up in the quickfix stack.
+/// ":cnewer [count]": Down in the quickfix stack.
+/// ":lolder [count]": Up in the location list stack.
+/// ":lnewer [count]": Down in the location list stack.
void qf_age(exarg_T *eap)
{
- qf_info_T *qi = &ql_info;
+ qf_info_T *qi;
int count;
- if (is_loclist_cmd(eap->cmdidx)) {
- qi = GET_LOC_LIST(curwin);
- if (qi == NULL) {
- EMSG(_(e_loclist));
- return;
- }
+ if ((qi = qf_cmd_get_stack(eap, true)) == NULL) {
+ return;
}
if (eap->addr_count != 0) {
@@ -2895,13 +3209,10 @@ void qf_age(exarg_T *eap)
/// Display the information about all the quickfix/location lists in the stack.
void qf_history(exarg_T *eap)
{
- qf_info_T *qi = &ql_info;
+ qf_info_T *qi = qf_cmd_get_stack(eap, false);
int i;
- if (is_loclist_cmd(eap->cmdidx)) {
- qi = GET_LOC_LIST(curwin);
- }
- if (qf_stack_empty(qi) || qf_list_empty(qi, qi->qf_curlist)) {
+ if (qf_stack_empty(qi) || qf_list_empty(qf_get_curlist(qi))) {
MSG(_("No entries"));
} else {
for (i = 0; i < qi->qf_listcount; i++) {
@@ -2912,12 +3223,11 @@ void qf_history(exarg_T *eap)
/// Free all the entries in the error list "idx". Note that other information
/// associated with the list like context and title are not freed.
-static void qf_free_items(qf_info_T *qi, int idx)
+static void qf_free_items(qf_list_T *qfl)
{
qfline_T *qfp;
qfline_T *qfpnext;
bool stop = false;
- qf_list_T *qfl = &qi->qf_lists[idx];
while (qfl->qf_count && qfl->qf_start != NULL) {
qfp = qfl->qf_start;
@@ -2958,10 +3268,9 @@ static void qf_free_items(qf_info_T *qi, int idx)
/// Free error list "idx". Frees all the entries in the quickfix list,
/// associated context information and the title.
-static void qf_free(qf_info_T *qi, int idx)
+static void qf_free(qf_list_T *qfl)
{
- qf_list_T *qfl = &qi->qf_lists[idx];
- qf_free_items(qi, idx);
+ qf_free_items(qfl);
XFREE_CLEAR(qfl->qf_title);
tv_free(qfl->qf_ctx);
@@ -2993,11 +3302,10 @@ 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 (i = 0, qfp = qi->qf_lists[idx].qf_start;
- i < qi->qf_lists[idx].qf_count && qfp != NULL;
- i++, qfp = qfp->qf_next) {
+ for (idx = 0; idx < qi->qf_listcount; idx++) {
+ qf_list_T *qfl = qf_get_list(qi, idx);
+ if (!qf_list_empty(qfl)) {
+ FOR_ALL_QFL_ITEMS(qfl, qfp, i) {
if (qfp->qf_fnum == curbuf->b_fnum) {
found_one = true;
if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2) {
@@ -3009,6 +3317,8 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount,
qfp->qf_lnum += amount_after;
}
}
+ }
+ }
return found_one;
}
@@ -3068,7 +3378,7 @@ void qf_view_result(bool split)
if (IS_LL_WINDOW(curwin)) {
qi = GET_LOC_LIST(curwin);
}
- if (qf_list_empty(qi, qi->qf_curlist)) {
+ if (qf_list_empty(qf_get_curlist(qi))) {
EMSG(_(e_quickfix));
return;
}
@@ -3096,15 +3406,16 @@ void qf_view_result(bool split)
*/
void ex_cwindow(exarg_T *eap)
{
- qf_info_T *qi = &ql_info;
+ qf_info_T *qi;
+ qf_list_T *qfl;
win_T *win;
- if (is_loclist_cmd(eap->cmdidx)) {
- qi = GET_LOC_LIST(curwin);
- if (qi == NULL)
- return;
+ if ((qi = qf_cmd_get_stack(eap, true)) == NULL) {
+ return;
}
+ qfl = qf_get_curlist(qi);
+
/* Look for an existing quickfix window. */
win = qf_find_win(qi);
@@ -3114,8 +3425,8 @@ void ex_cwindow(exarg_T *eap)
* it if we have errors; otherwise, leave it closed.
*/
if (qf_stack_empty(qi)
- || qi->qf_lists[qi->qf_curlist].qf_nonevalid
- || qf_list_empty(qi, qi->qf_curlist)) {
+ || qfl->qf_nonevalid
+ || qf_list_empty(qf_get_curlist(qi))) {
if (win != NULL) {
ex_cclose(eap);
}
@@ -3130,13 +3441,11 @@ void ex_cwindow(exarg_T *eap)
*/
void ex_cclose(exarg_T *eap)
{
- win_T *win = NULL;
- qf_info_T *qi = &ql_info;
+ win_T *win = NULL;
+ qf_info_T *qi;
- if (is_loclist_cmd(eap->cmdidx)) {
- qi = GET_LOC_LIST(curwin);
- if (qi == NULL)
- return;
+ if ((qi = qf_cmd_get_stack(eap, false)) == NULL) {
+ return;
}
/* Find existing quickfix window and close it. */
@@ -3240,24 +3549,30 @@ static int qf_open_new_cwindow(const qf_info_T *qi, int height)
return OK;
}
-/*
- * ":copen": open a window that shows the list of errors.
- * ":lopen": open a window that shows the location list.
- */
+/// Set "w:quickfix_title" if "qi" has a title.
+static void qf_set_title_var(qf_list_T *qfl)
+{
+ if (qfl->qf_title != NULL) {
+ set_internal_string_var((char_u *)"w:quickfix_title", qfl->qf_title);
+ }
+}
+
+/// ":copen": open a window that shows the list of errors.
+/// ":lopen": open a window that shows the location list.
void ex_copen(exarg_T *eap)
{
- qf_info_T *qi = &ql_info;
+ qf_info_T *qi;
+ qf_list_T *qfl;
int height;
int status = FAIL;
+ int lnum;
- if (is_loclist_cmd(eap->cmdidx)) {
- qi = GET_LOC_LIST(curwin);
- if (qi == NULL) {
- EMSG(_(e_loclist));
- return;
- }
+ if ((qi = qf_cmd_get_stack(eap, true)) == NULL) {
+ return;
}
+ incr_quickfix_busy();
+
if (eap->addr_count != 0) {
assert(eap->line2 <= INT_MAX);
height = (int)eap->line2;
@@ -3273,16 +3588,23 @@ void ex_copen(exarg_T *eap)
}
if (status == FAIL) {
if (qf_open_new_cwindow(qi, height) == FAIL) {
+ decr_quickfix_busy();
return;
}
}
- qf_set_title_var(qi);
+ qfl = qf_get_curlist(qi);
+ qf_set_title_var(qfl);
+ // Save the current index here, as updating the quickfix buffer may free
+ // the quickfix list
+ lnum = qfl->qf_index;
// Fill the buffer with the quickfix list.
- qf_fill_buffer(qi, curbuf, NULL);
+ qf_fill_buffer(qfl, curbuf, NULL);
+
+ decr_quickfix_busy();
- curwin->w_cursor.lnum = qi->qf_lists[qi->qf_curlist].qf_index;
+ curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = 0;
check_cursor();
update_topline(); // scroll to show the line
@@ -3306,17 +3628,13 @@ static void qf_win_goto(win_T *win, linenr_T lnum)
curbuf = curwin->w_buffer;
}
-// :cbottom/:lbottom command.
+/// :cbottom/:lbottom command.
void ex_cbottom(exarg_T *eap)
{
- qf_info_T *qi = &ql_info;
+ qf_info_T *qi;
- if (is_loclist_cmd(eap->cmdidx)) {
- qi = GET_LOC_LIST(curwin);
- if (qi == NULL) {
- EMSG(_(e_loclist));
- return;
- }
+ if ((qi = qf_cmd_get_stack(eap, true)) == NULL) {
+ return;
}
win_T *win = qf_find_win(qi);
@@ -3338,7 +3656,7 @@ linenr_T qf_current_entry(win_T *wp)
/* In the location list window, use the referenced location list */
qi = wp->w_llist_ref;
- return qi->qf_lists[qi->qf_curlist].qf_index;
+ return qf_get_curlist(qi)->qf_index;
}
/*
@@ -3352,7 +3670,7 @@ qf_win_pos_update (
)
{
win_T *win;
- int qf_index = qi->qf_lists[qi->qf_curlist].qf_index;
+ int qf_index = qf_get_curlist(qi)->qf_index;
/*
* Put the cursor on the current error in the quickfix window, so that
@@ -3375,7 +3693,7 @@ qf_win_pos_update (
}
/// Checks whether the given window is displaying the specified
-/// quickfix/location list buffer.
+/// quickfix/location stack.
static int is_qf_win(const win_T *win, const qf_info_T *qi)
FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -3395,7 +3713,7 @@ static int is_qf_win(const win_T *win, const qf_info_T *qi)
return false;
}
-/// Find a window displaying the quickfix/location list 'qi'
+/// Find a window displaying the quickfix/location stack 'qi'
/// Only searches in the current tabpage.
static win_T *qf_find_win(const qf_info_T *qi)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
@@ -3433,7 +3751,7 @@ static void qf_update_win_titlevar(qf_info_T *qi)
if ((win = qf_find_win(qi)) != NULL) {
win_T *curwin_save = curwin;
curwin = win;
- qf_set_title_var(qi);
+ qf_set_title_var(qf_get_curlist(qi));
curwin = curwin_save;
}
}
@@ -3459,7 +3777,7 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
qf_update_win_titlevar(qi);
- qf_fill_buffer(qi, buf, old_last);
+ qf_fill_buffer(qf_get_curlist(qi), buf, old_last);
buf_inc_changedtick(buf);
if (old_last == NULL) {
@@ -3477,15 +3795,6 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
}
}
-// Set "w:quickfix_title" if "qi" has a title.
-static void qf_set_title_var(qf_info_T *qi)
-{
- if (qi->qf_lists[qi->qf_curlist].qf_title != NULL) {
- set_internal_string_var((char_u *)"w:quickfix_title",
- qi->qf_lists[qi->qf_curlist].qf_title);
- }
-}
-
// Add an error line to the quickfix buffer.
static int qf_buf_add_line(buf_T *buf, linenr_T lnum, const qfline_T *qfp,
char_u *dirname)
@@ -3552,13 +3861,13 @@ static int qf_buf_add_line(buf_T *buf, linenr_T lnum, const qfline_T *qfp,
return OK;
}
-// 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)
- FUNC_ATTR_NONNULL_ARG(1, 2)
+/// 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_list_T *qfl, buf_T *buf, qfline_T *old_last)
+ FUNC_ATTR_NONNULL_ARG(2)
{
linenr_T lnum;
qfline_T *qfp;
@@ -3577,20 +3886,20 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last)
}
// Check if there is anything to display
- if (!qf_stack_empty(qi)) {
+ if (qfl != NULL) {
char_u dirname[MAXPATHL];
*dirname = NUL;
// Add one line for each error
if (old_last == NULL) {
- qfp = qi->qf_lists[qi->qf_curlist].qf_start;
+ qfp = qfl->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) {
+ while (lnum < qfl->qf_count) {
if (qf_buf_add_line(buf, lnum, qfp, dirname) == FAIL) {
break;
}
@@ -3633,9 +3942,9 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last)
KeyTyped = old_KeyTyped;
}
-static void qf_list_changed(qf_info_T *qi, int qf_idx)
+static void qf_list_changed(qf_list_T *qfl)
{
- qi->qf_lists[qf_idx].qf_changedtick++;
+ qfl->qf_changedtick++;
}
/// Return the quickfix/location list number with the given identifier.
@@ -3651,15 +3960,15 @@ static int qf_id2nr(const qf_info_T *const qi, const unsigned qfid)
return INVALID_QFIDX;
}
-// If the current list is not "save_qfid" and we can find the list with that ID
-// then make it the current list.
-// This is used when autocommands may have changed the current list.
-// Returns OK if successfully restored the list. Returns FAIL if the list with
-// the specified identifier (save_qfid) is not found in the stack.
+/// If the current list is not "save_qfid" and we can find the list with that ID
+/// then make it the current list.
+/// This is used when autocommands may have changed the current list.
+/// Returns OK if successfully restored the list. Returns FAIL if the list with
+/// the specified identifier (save_qfid) is not found in the stack.
static int qf_restore_list(qf_info_T *qi, unsigned save_qfid)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- if (qi->qf_lists[qi->qf_curlist].qf_id != save_qfid) {
+ if (qf_get_curlist(qi)->qf_id != save_qfid) {
const int curlist = qf_id2nr(qi, save_qfid);
if (curlist < 0) {
// list is not present
@@ -3678,7 +3987,7 @@ static void qf_jump_first(qf_info_T *qi, unsigned save_qfid, int forceit)
return;
}
// Autocommands might have cleared the list, check for that
- if (!qf_list_empty(qi, qi->qf_curlist)) {
+ if (!qf_list_empty(qf_get_curlist(qi))) {
qf_jump(qi, 0, 0, forceit);
}
}
@@ -3787,6 +4096,7 @@ void ex_make(exarg_T *eap)
do_shell((char_u *)cmd, 0);
+ incr_quickfix_busy();
res = qf_init(wp, fname, (eap->cmdidx != CMD_make
&& eap->cmdidx != CMD_lmake) ? p_gefm : p_efm,
@@ -3799,11 +4109,11 @@ void ex_make(exarg_T *eap)
}
}
if (res >= 0) {
- qf_list_changed(qi, qi->qf_curlist);
+ qf_list_changed(qf_get_curlist(qi));
}
// Remember the current quickfix list identifier, so that we can
// check for autocommands changing the current quickfix list.
- unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
+ unsigned save_qfid = qf_get_curlist(qi)->qf_id;
if (au_name != NULL) {
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, true,
curbuf);
@@ -3814,6 +4124,7 @@ void ex_make(exarg_T *eap)
}
cleanup:
+ decr_quickfix_busy();
os_remove((char *)fname);
xfree(fname);
xfree(cmd);
@@ -3871,23 +4182,20 @@ static char_u *get_mef_name(void)
size_t qf_get_size(exarg_T *eap)
FUNC_ATTR_NONNULL_ALL
{
- qf_info_T *qi = &ql_info;
- if (is_loclist_cmd(eap->cmdidx)) {
- // Location list.
- qi = GET_LOC_LIST(curwin);
- if (qi == NULL) {
- return 0;
- }
+ qf_info_T *qi;
+ qf_list_T *qfl;
+
+ if ((qi = qf_cmd_get_stack(eap, false)) == NULL) {
+ return 0;
}
int prev_fnum = 0;
size_t sz = 0;
qfline_T *qfp;
- size_t i;
- assert(qi->qf_lists[qi->qf_curlist].qf_count >= 0);
- for (i = 0, qfp = qi->qf_lists[qi->qf_curlist].qf_start;
- i < (size_t)qi->qf_lists[qi->qf_curlist].qf_count && qfp != NULL;
- i++, qfp = qfp->qf_next) {
+ int i;
+ assert(qf_get_curlist(qi)->qf_count >= 0);
+ qfl = qf_get_curlist(qi);
+ FOR_ALL_QFL_ITEMS(qfl, qfp, i) {
if (!qfp->qf_valid) {
continue;
}
@@ -3910,18 +4218,14 @@ size_t qf_get_size(exarg_T *eap)
size_t qf_get_cur_idx(exarg_T *eap)
FUNC_ATTR_NONNULL_ALL
{
- qf_info_T *qi = &ql_info;
+ qf_info_T *qi;
- if (is_loclist_cmd(eap->cmdidx)) {
- // Location list.
- qi = GET_LOC_LIST(curwin);
- if (qi == NULL) {
- return 0;
- }
+ if ((qi = qf_cmd_get_stack(eap, false)) == NULL) {
+ return 0;
}
- assert(qi->qf_lists[qi->qf_curlist].qf_index >= 0);
- return (size_t)qi->qf_lists[qi->qf_curlist].qf_index;
+ assert(qf_get_curlist(qi)->qf_index >= 0);
+ return (size_t)qf_get_curlist(qi)->qf_index;
}
/// Returns the current index in the quickfix/location list,
@@ -3930,20 +4234,16 @@ size_t qf_get_cur_idx(exarg_T *eap)
int qf_get_cur_valid_idx(exarg_T *eap)
FUNC_ATTR_NONNULL_ALL
{
- qf_info_T *qi = &ql_info;
+ qf_info_T *qi;
- if (is_loclist_cmd(eap->cmdidx)) {
- // Location list.
- qi = GET_LOC_LIST(curwin);
- if (qi == NULL) {
- return 1;
- }
+ if ((qi = qf_cmd_get_stack(eap, false)) == NULL) {
+ return 1;
}
- qf_list_T *qfl = &qi->qf_lists[qi->qf_curlist];
+ qf_list_T *qfl = qf_get_curlist(qi);
// Check if the list has valid errors.
- if (qfl->qf_count <= 0 || qfl->qf_nonevalid) {
+ if (!qf_list_has_valid_entries(qfl)) {
return 1;
}
@@ -3978,24 +4278,20 @@ int qf_get_cur_valid_idx(exarg_T *eap)
/// Used by :cdo, :ldo, :cfdo and :lfdo commands.
/// For :cdo and :ldo, returns the 'n'th valid error entry.
/// For :cfdo and :lfdo, returns the 'n'th valid file entry.
-static size_t qf_get_nth_valid_entry(qf_info_T *qi, size_t n, bool fdo)
+static size_t qf_get_nth_valid_entry(qf_list_T *qfl, size_t n, int fdo)
FUNC_ATTR_NONNULL_ALL
{
- qf_list_T *qfl = &qi->qf_lists[qi->qf_curlist];
-
// Check if the list has valid errors.
- if (qfl->qf_count <= 0 || qfl->qf_nonevalid) {
+ if (!qf_list_has_valid_entries(qfl)) {
return 1;
}
int prev_fnum = 0;
size_t eidx = 0;
- size_t i;
+ int i;
qfline_T *qfp;
assert(qfl->qf_count >= 0);
- for (i = 1, qfp = qfl->qf_start;
- i <= (size_t)qfl->qf_count && qfp != NULL;
- i++, qfp = qfp->qf_next) {
+ FOR_ALL_QFL_ITEMS(qfl, qfp, i) {
if (qfp->qf_valid) {
if (fdo) {
if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) {
@@ -4013,24 +4309,18 @@ static size_t qf_get_nth_valid_entry(qf_info_T *qi, size_t n, bool fdo)
}
}
- return i <= (size_t)qfl->qf_count ? i : 1;
+ return i <= qfl->qf_count ? (size_t)i : 1;
}
-/*
- * ":cc", ":crewind", ":cfirst" and ":clast".
- * ":ll", ":lrewind", ":lfirst" and ":llast".
- * ":cdo", ":ldo", ":cfdo" and ":lfdo".
- */
+/// ":cc", ":crewind", ":cfirst" and ":clast".
+/// ":ll", ":lrewind", ":lfirst" and ":llast".
+/// ":cdo", ":ldo", ":cfdo" and ":lfdo".
void ex_cc(exarg_T *eap)
{
- qf_info_T *qi = &ql_info;
+ qf_info_T *qi;
- if (is_loclist_cmd(eap->cmdidx)) {
- qi = GET_LOC_LIST(curwin);
- if (qi == NULL) {
- EMSG(_(e_loclist));
- return;
- }
+ if ((qi = qf_cmd_get_stack(eap, true)) == NULL) {
+ return;
}
int errornr;
@@ -4065,8 +4355,9 @@ void ex_cc(exarg_T *eap)
} else {
n = 1;
}
- size_t valid_entry = qf_get_nth_valid_entry(qi, n,
- eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo);
+ size_t valid_entry = qf_get_nth_valid_entry(
+ qf_get_curlist(qi), n,
+ eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo);
assert(valid_entry <= INT_MAX);
errornr = (int)valid_entry;
}
@@ -4074,21 +4365,15 @@ void ex_cc(exarg_T *eap)
qf_jump(qi, 0, errornr, eap->forceit);
}
-/*
- * ":cnext", ":cnfile", ":cNext" and ":cprevious".
- * ":lnext", ":lNext", ":lprevious", ":lnfile", ":lNfile" and ":lpfile".
- * ":cdo", ":ldo", ":cfdo" and ":lfdo".
- */
+/// ":cnext", ":cnfile", ":cNext" and ":cprevious".
+/// ":lnext", ":lNext", ":lprevious", ":lnfile", ":lNfile" and ":lpfile".
+/// ":cdo", ":ldo", ":cfdo" and ":lfdo".
void ex_cnext(exarg_T *eap)
{
- qf_info_T *qi = &ql_info;
+ qf_info_T *qi;
- if (is_loclist_cmd(eap->cmdidx)) {
- qi = GET_LOC_LIST(curwin);
- if (qi == NULL) {
- EMSG(_(e_loclist));
- return;
- }
+ if ((qi = qf_cmd_get_stack(eap, true)) == NULL) {
+ return;
}
int errornr;
@@ -4133,6 +4418,297 @@ void ex_cnext(exarg_T *eap)
qf_jump(qi, dir, errornr, eap->forceit);
}
+/// Find the first entry in the quickfix list 'qfl' from buffer 'bnr'.
+/// The index of the entry is stored in 'errornr'.
+/// Returns NULL if an entry is not found.
+static qfline_T *qf_find_first_entry_in_buf(qf_list_T *qfl,
+ int bnr,
+ int *errornr)
+{
+ qfline_T *qfp = NULL;
+ int idx = 0;
+
+ // Find the first entry in this file
+ FOR_ALL_QFL_ITEMS(qfl, qfp, idx) {
+ if (qfp->qf_fnum == bnr) {
+ break;
+ }
+ }
+
+ *errornr = idx;
+ return qfp;
+}
+
+/// Find the first quickfix entry on the same line as 'entry'. Updates 'errornr'
+/// with the error number for the first entry. Assumes the entries are sorted in
+/// the quickfix list by line number.
+static qfline_T * qf_find_first_entry_on_line(qfline_T *entry, int *errornr)
+{
+ while (!got_int
+ && entry->qf_prev != NULL
+ && entry->qf_fnum == entry->qf_prev->qf_fnum
+ && entry->qf_lnum == entry->qf_prev->qf_lnum) {
+ entry = entry->qf_prev;
+ (*errornr)--;
+ }
+
+ return entry;
+}
+
+/// Find the last quickfix entry on the same line as 'entry'. Updates 'errornr'
+/// with the error number for the last entry. Assumes the entries are sorted in
+/// the quickfix list by line number.
+static qfline_T * qf_find_last_entry_on_line(qfline_T *entry, int *errornr)
+{
+ while (!got_int
+ && entry->qf_next != NULL
+ && entry->qf_fnum == entry->qf_next->qf_fnum
+ && entry->qf_lnum == entry->qf_next->qf_lnum) {
+ entry = entry->qf_next;
+ (*errornr)++;
+ }
+
+ return entry;
+}
+
+/// Find the first quickfix entry below line 'lnum' in buffer 'bnr'.
+/// 'qfp' points to the very first entry in the buffer and 'errornr' is the
+/// index of the very first entry in the quickfix list.
+/// Returns NULL if an entry is not found after 'lnum'.
+static qfline_T *qf_find_entry_on_next_line(int bnr,
+ linenr_T lnum,
+ qfline_T *qfp,
+ int *errornr)
+{
+ if (qfp->qf_lnum > lnum) {
+ // First entry is after line 'lnum'
+ return qfp;
+ }
+
+ // Find the entry just before or at the line 'lnum'
+ while (qfp->qf_next != NULL
+ && qfp->qf_next->qf_fnum == bnr
+ && qfp->qf_next->qf_lnum <= lnum) {
+ qfp = qfp->qf_next;
+ (*errornr)++;
+ }
+
+ if (qfp->qf_next == NULL || qfp->qf_next->qf_fnum != bnr) {
+ // No entries found after 'lnum'
+ return NULL;
+ }
+
+ // Use the entry just after line 'lnum'
+ qfp = qfp->qf_next;
+ (*errornr)++;
+
+ return qfp;
+}
+
+/// Find the first quickfix entry before line 'lnum' in buffer 'bnr'.
+/// 'qfp' points to the very first entry in the buffer and 'errornr' is the
+/// index of the very first entry in the quickfix list.
+/// Returns NULL if an entry is not found before 'lnum'.
+static qfline_T *qf_find_entry_on_prev_line(int bnr,
+ linenr_T lnum,
+ qfline_T *qfp,
+ int *errornr)
+{
+ // Find the entry just before the line 'lnum'
+ while (qfp->qf_next != NULL
+ && qfp->qf_next->qf_fnum == bnr
+ && qfp->qf_next->qf_lnum < lnum) {
+ qfp = qfp->qf_next;
+ (*errornr)++;
+ }
+
+ if (qfp->qf_lnum >= lnum) { // entry is after 'lnum'
+ return NULL;
+ }
+
+ // If multiple entries are on the same line, then use the first entry
+ qfp = qf_find_first_entry_on_line(qfp, errornr);
+
+ return qfp;
+}
+
+/// Find a quickfix entry in 'qfl' closest to line 'lnum' in buffer 'bnr' in
+/// the direction 'dir'.
+static qfline_T *qf_find_closest_entry(qf_list_T *qfl,
+ int bnr,
+ linenr_T lnum,
+ int dir,
+ int *errornr)
+{
+ qfline_T *qfp;
+
+ *errornr = 0;
+
+ // Find the first entry in this file
+ qfp = qf_find_first_entry_in_buf(qfl, bnr, errornr);
+ if (qfp == NULL) {
+ return NULL; // no entry in this file
+ }
+
+ if (dir == FORWARD) {
+ qfp = qf_find_entry_on_next_line(bnr, lnum, qfp, errornr);
+ } else {
+ qfp = qf_find_entry_on_prev_line(bnr, lnum, qfp, errornr);
+ }
+
+ return qfp;
+}
+
+/// Get the nth quickfix entry below the specified entry treating multiple
+/// entries on a single line as one. Searches forward in the list.
+static qfline_T *qf_get_nth_below_entry(qfline_T *entry,
+ int *errornr,
+ linenr_T n)
+{
+ while (n-- > 0 && !got_int) {
+ qfline_T *first_entry = entry;
+ int first_errornr = *errornr;
+
+ // Treat all the entries on the same line in this file as one
+ entry = qf_find_last_entry_on_line(entry, errornr);
+
+ if (entry->qf_next == NULL
+ || entry->qf_next->qf_fnum != entry->qf_fnum) {
+ // If multiple entries are on the same line, then use the first
+ // entry
+ entry = first_entry;
+ *errornr = first_errornr;
+ break;
+ }
+
+ entry = entry->qf_next;
+ (*errornr)++;
+ }
+
+ return entry;
+}
+
+/// Get the nth quickfix entry above the specified entry treating multiple
+/// entries on a single line as one. Searches backwards in the list.
+static qfline_T *qf_get_nth_above_entry(qfline_T *entry,
+ int *errornr,
+ linenr_T n)
+{
+ while (n-- > 0 && !got_int) {
+ if (entry->qf_prev == NULL
+ || entry->qf_prev->qf_fnum != entry->qf_fnum) {
+ break;
+ }
+
+ entry = entry->qf_prev;
+ (*errornr)--;
+
+ // If multiple entries are on the same line, then use the first entry
+ entry = qf_find_first_entry_on_line(entry, errornr);
+ }
+
+ return entry;
+}
+
+/// Find the n'th quickfix entry adjacent to line 'lnum' in buffer 'bnr' in the
+/// specified direction.
+/// Returns the error number in the quickfix list or 0 if an entry is not found.
+static int qf_find_nth_adj_entry(qf_list_T *qfl,
+ int bnr,
+ linenr_T lnum,
+ linenr_T n,
+ int dir)
+{
+ qfline_T *adj_entry;
+ int errornr;
+
+ // Find an entry closest to the specified line
+ adj_entry = qf_find_closest_entry(qfl, bnr, lnum, dir, &errornr);
+ if (adj_entry == NULL) {
+ return 0;
+ }
+
+ if (--n > 0) {
+ // Go to the n'th entry in the current buffer
+ if (dir == FORWARD) {
+ adj_entry = qf_get_nth_below_entry(adj_entry, &errornr, n);
+ } else {
+ adj_entry = qf_get_nth_above_entry(adj_entry, &errornr, n);
+ }
+ }
+
+ return errornr;
+}
+
+/// Jump to a quickfix entry in the current file nearest to the current line.
+/// ":cabove", ":cbelow", ":labove" and ":lbelow" commands
+void ex_cbelow(exarg_T *eap)
+{
+ qf_info_T *qi;
+ qf_list_T *qfl;
+ int dir;
+ int buf_has_flag;
+ int errornr = 0;
+
+ if (eap->addr_count > 0 && eap->line2 <= 0) {
+ EMSG(_(e_invrange));
+ return;
+ }
+
+ // Check whether the current buffer has any quickfix entries
+ if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow) {
+ buf_has_flag = BUF_HAS_QF_ENTRY;
+ } else {
+ buf_has_flag = BUF_HAS_LL_ENTRY;
+ }
+ if (!(curbuf->b_has_qf_entry & buf_has_flag)) {
+ EMSG(_(e_quickfix));
+ return;
+ }
+
+ if ((qi = qf_cmd_get_stack(eap, true)) == NULL) {
+ return;
+ }
+
+ qfl = qf_get_curlist(qi);
+ // check if the list has valid errors
+ if (!qf_list_has_valid_entries(qfl)) {
+ EMSG(_(e_quickfix));
+ return;
+ }
+
+ if (eap->cmdidx == CMD_cbelow || eap->cmdidx == CMD_lbelow) {
+ dir = FORWARD;
+ } else {
+ dir = BACKWARD;
+ }
+
+ errornr = qf_find_nth_adj_entry(qfl, curbuf->b_fnum, curwin->w_cursor.lnum,
+ eap->addr_count > 0 ? eap->line2 : 0, dir);
+
+ if (errornr > 0) {
+ qf_jump(qi, 0, errornr, false);
+ } else {
+ EMSG(_(e_no_more_items));
+ }
+}
+
+
+/// Return the autocmd name for the :cfile Ex commands
+static char_u * cfile_get_auname(cmdidx_T cmdidx)
+{
+ switch (cmdidx) {
+ case CMD_cfile: return (char_u *)"cfile";
+ case CMD_cgetfile: return (char_u *)"cgetfile";
+ case CMD_caddfile: return (char_u *)"caddfile";
+ case CMD_lfile: return (char_u *)"lfile";
+ case CMD_lgetfile: return (char_u *)"lgetfile";
+ case CMD_laddfile: return (char_u *)"laddfile";
+ default: return NULL;
+ }
+}
+
+
/*
* ":cfile"/":cgetfile"/":caddfile" commands.
* ":lfile"/":lgetfile"/":laddfile" commands.
@@ -4143,15 +4719,7 @@ void ex_cfile(exarg_T *eap)
qf_info_T *qi = &ql_info;
char_u *au_name = NULL;
- switch (eap->cmdidx) {
- case CMD_cfile: au_name = (char_u *)"cfile"; break;
- case CMD_cgetfile: au_name = (char_u *)"cgetfile"; break;
- case CMD_caddfile: au_name = (char_u *)"caddfile"; break;
- case CMD_lfile: au_name = (char_u *)"lfile"; break;
- case CMD_lgetfile: au_name = (char_u *)"lgetfile"; break;
- case CMD_laddfile: au_name = (char_u *)"laddfile"; break;
- default: break;
- }
+ au_name = cfile_get_auname(eap->cmdidx);
if (au_name != NULL
&& apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, NULL, false, curbuf)) {
if (aborting()) {
@@ -4168,6 +4736,8 @@ void ex_cfile(exarg_T *eap)
wp = curwin;
}
+ incr_quickfix_busy();
+
// This function is used by the :cfile, :cgetfile and :caddfile
// commands.
// :cfile always creates a new quickfix list and jumps to the
@@ -4182,13 +4752,14 @@ void ex_cfile(exarg_T *eap)
if (wp != NULL) {
qi = GET_LOC_LIST(wp);
if (qi == NULL) {
+ decr_quickfix_busy();
return;
}
}
if (res >= 0) {
- qf_list_changed(qi, qi->qf_curlist);
+ qf_list_changed(qf_get_curlist(qi));
}
- unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
+ unsigned save_qfid = qf_get_curlist(qi)->qf_id;
if (au_name != NULL) {
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, false, curbuf);
}
@@ -4199,6 +4770,8 @@ void ex_cfile(exarg_T *eap)
// display the first error
qf_jump_first(qi, save_qfid, eap->forceit);
}
+
+ decr_quickfix_busy();
}
/// Return the vimgrep autocmd name.
@@ -4319,8 +4892,7 @@ static bool vgr_match_buflines(qf_info_T *qi, char_u *fname, buf_T *buf,
// Pass the buffer number so that it gets used even for a
// dummy buffer, unless duplicate_name is set, then the
// buffer will be wiped out below.
- if (qf_add_entry(qi,
- qi->qf_curlist,
+ if (qf_add_entry(qf_get_curlist(qi),
NULL, // dir
fname,
NULL,
@@ -4333,8 +4905,8 @@ static bool vgr_match_buflines(qf_info_T *qi, char_u *fname, buf_T *buf,
NULL, // search pattern
0, // nr
0, // type
- true // valid
- ) == FAIL) {
+ true) // valid
+ == QF_FAIL) {
got_int = true;
break;
}
@@ -4396,7 +4968,8 @@ void ex_vimgrep(exarg_T *eap)
char_u *s;
char_u *p;
int fi;
- qf_info_T *qi = &ql_info;
+ qf_info_T *qi;
+ qf_list_T *qfl;
win_T *wp = NULL;
buf_T *buf;
int duplicate_name = FALSE;
@@ -4421,9 +4994,9 @@ void ex_vimgrep(exarg_T *eap)
}
}
- if (is_loclist_cmd(eap->cmdidx)) {
- qi = ll_get_or_alloc_list(curwin);
- wp = curwin;
+ qi = qf_cmd_get_or_alloc_stack(eap, &wp);
+ if (qi == NULL) {
+ return;
}
if (eap->addr_count > 0)
@@ -4473,9 +5046,11 @@ void ex_vimgrep(exarg_T *eap)
* ":lcd %:p:h" changes the meaning of short path names. */
os_dirname(dirname_start, MAXPATHL);
+ incr_quickfix_busy();
+
// Remember the current quickfix list identifier, so that we can check for
// autocommands changing the current quickfix list.
- unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
+ unsigned save_qfid = qf_get_curlist(qi)->qf_id;
seconds = (time_t)0;
for (fi = 0; fi < fcount && !got_int && tomatch > 0; fi++) {
@@ -4504,9 +5079,10 @@ void ex_vimgrep(exarg_T *eap)
// buffer above, autocommands might have changed the quickfix list.
if (!vgr_qflist_valid(wp, qi, save_qfid, *eap->cmdlinep)) {
FreeWild(fcount, fnames);
+ decr_quickfix_busy();
goto theend;
}
- save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
+ save_qfid = qf_get_curlist(qi)->qf_id;
if (buf == NULL) {
if (!got_int)
@@ -4574,10 +5150,11 @@ void ex_vimgrep(exarg_T *eap)
FreeWild(fcount, fnames);
- 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;
- qi->qf_lists[qi->qf_curlist].qf_index = 1;
- qf_list_changed(qi, qi->qf_curlist);
+ qfl = qf_get_curlist(qi);
+ qfl->qf_nonevalid = false;
+ qfl->qf_ptr = qfl->qf_start;
+ qfl->qf_index = 1;
+ qf_list_changed(qfl);
qf_update_buffer(qi, NULL);
@@ -4587,16 +5164,14 @@ void ex_vimgrep(exarg_T *eap)
// The QuickFixCmdPost autocmd may free the quickfix list. Check the list
// is still valid.
- if (!qflist_valid(wp, save_qfid)) {
- goto theend;
- }
-
- if (qf_restore_list(qi, save_qfid) == FAIL) {
+ if (!qflist_valid(wp, save_qfid)
+ || qf_restore_list(qi, save_qfid) == FAIL) {
+ decr_quickfix_busy();
goto theend;
}
// Jump to first match.
- if (qi->qf_lists[qi->qf_curlist].qf_count > 0) {
+ if (!qf_list_empty(qf_get_curlist(qi))) {
if ((flags & VGR_NOJUMP) == 0) {
vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy, first_match_buf,
target_dir);
@@ -4604,6 +5179,8 @@ void ex_vimgrep(exarg_T *eap)
} else
EMSG2(_(e_nomatch2), s);
+ decr_quickfix_busy();
+
/* If we loaded a dummy buffer into the current window, the autocommands
* may have messed up things, need to redraw and recompute folds. */
if (redraw_for_dummy) {
@@ -4780,15 +5357,62 @@ static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start)
}
}
+/// Copy the specified quickfix entry items into a new dict and appened the dict
+/// to 'list'. Returns OK on success.
+static int get_qfline_items(qfline_T *qfp, list_T *list)
+{
+ char_u buf[2];
+ int bufnum;
+
+ // Handle entries with a non-existing buffer number.
+ bufnum = qfp->qf_fnum;
+ if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) {
+ bufnum = 0;
+ }
+
+ dict_T *const dict = tv_dict_alloc();
+ tv_list_append_dict(list, dict);
+
+ buf[0] = qfp->qf_type;
+ buf[1] = NUL;
+ if (tv_dict_add_nr(dict, S_LEN("bufnr"), (varnumber_T)bufnum) == FAIL
+ || (tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)qfp->qf_lnum)
+ == FAIL)
+ || (tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)qfp->qf_col) == FAIL)
+ || (tv_dict_add_nr(dict, S_LEN("vcol"), (varnumber_T)qfp->qf_viscol)
+ == FAIL)
+ || (tv_dict_add_nr(dict, S_LEN("nr"), (varnumber_T)qfp->qf_nr) == FAIL)
+ || (tv_dict_add_str(
+ dict, S_LEN("module"),
+ (qfp->qf_module == NULL ? "" : (const char *)qfp->qf_module))
+ == FAIL)
+ || (tv_dict_add_str(
+ dict, S_LEN("pattern"),
+ (qfp->qf_pattern == NULL ? "" : (const char *)qfp->qf_pattern))
+ == FAIL)
+ || (tv_dict_add_str(
+ dict, S_LEN("text"),
+ (qfp->qf_text == NULL ? "" : (const char *)qfp->qf_text))
+ == FAIL)
+ || (tv_dict_add_str(dict, S_LEN("type"), (const char *)buf) == FAIL)
+ || (tv_dict_add_nr(dict, S_LEN("valid"), (varnumber_T)qfp->qf_valid)
+ == FAIL)) {
+ // tv_dict_add* fail only if key already exist, but this is a newly
+ // allocated dictionary which is thus guaranteed to have no existing keys.
+ assert(false);
+ }
+
+ return OK;
+}
+
/// Add each quickfix error to list "list" as a dictionary.
/// If qf_idx is -1, use the current list. Otherwise, use the specified list.
-int get_errorlist(const qf_info_T *qi_arg, win_T *wp, int qf_idx, list_T *list)
+int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, list_T *list)
{
- const qf_info_T *qi = qi_arg;
- char_u buf[2];
+ qf_info_T *qi = qi_arg;
+ qf_list_T *qfl;
qfline_T *qfp;
int i;
- int bufnum;
if (qi == NULL) {
qi = &ql_info;
@@ -4804,56 +5428,19 @@ int get_errorlist(const qf_info_T *qi_arg, win_T *wp, int qf_idx, list_T *list)
qf_idx = qi->qf_curlist;
}
- if (qf_idx >= qi->qf_listcount
- || qi->qf_lists[qf_idx].qf_count == 0) {
+ if (qf_idx >= qi->qf_listcount) {
return FAIL;
}
- qfp = qi->qf_lists[qf_idx].qf_start;
- for (i = 1; !got_int && i <= qi->qf_lists[qf_idx].qf_count; i++) {
- // Handle entries with a non-existing buffer number.
- bufnum = qfp->qf_fnum;
- if (bufnum != 0 && (buflist_findnr(bufnum) == NULL))
- bufnum = 0;
-
- dict_T *const dict = tv_dict_alloc();
- tv_list_append_dict(list, dict);
-
- buf[0] = qfp->qf_type;
- buf[1] = NUL;
- if (tv_dict_add_nr(dict, S_LEN("bufnr"), (varnumber_T)bufnum) == FAIL
- || (tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)qfp->qf_lnum)
- == FAIL)
- || (tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)qfp->qf_col)
- == FAIL)
- || (tv_dict_add_nr(dict, S_LEN("vcol"), (varnumber_T)qfp->qf_viscol)
- == FAIL)
- || (tv_dict_add_nr(dict, S_LEN("nr"), (varnumber_T)qfp->qf_nr) == FAIL)
- || tv_dict_add_str(dict, S_LEN("module"),
- (qfp->qf_module == NULL
- ? ""
- : (const char *)qfp->qf_module)) == FAIL
- || tv_dict_add_str(dict, S_LEN("pattern"),
- (qfp->qf_pattern == NULL
- ? ""
- : (const char *)qfp->qf_pattern)) == FAIL
- || tv_dict_add_str(dict, S_LEN("text"),
- (qfp->qf_text == NULL
- ? ""
- : (const char *)qfp->qf_text)) == FAIL
- || tv_dict_add_str(dict, S_LEN("type"), (const char *)buf) == FAIL
- || (tv_dict_add_nr(dict, S_LEN("valid"), (varnumber_T)qfp->qf_valid)
- == FAIL)) {
- // tv_dict_add* fail only if key already exist, but this is a newly
- // allocated dictionary which is thus guaranteed to have no existing keys.
- assert(false);
- }
+ qfl = qf_get_list(qi, qf_idx);
+ if (qf_list_empty(qfl)) {
+ return FAIL;
+ }
- qfp = qfp->qf_next;
- if (qfp == NULL) {
- break;
- }
+ FOR_ALL_QFL_ITEMS(qfl, qfp, i) {
+ get_qfline_items(qfp, list);
}
+
return OK;
}
@@ -4894,12 +5481,12 @@ static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict)
}
list_T *l = tv_list_alloc(kListLenMayKnow);
- qf_info_T *const qi = ll_new_list();
+ qf_info_T *const qi = qf_alloc_stack(QFLT_INTERNAL);
if (qf_init_ext(qi, 0, NULL, NULL, &di->di_tv, errorformat,
true, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) {
(void)get_errorlist(qi, NULL, 0, l);
- qf_free(qi, 0);
+ qf_free(&qi->qf_lists[0]);
}
xfree(qi);
@@ -5021,7 +5608,10 @@ static int qf_getprop_qfidx(qf_info_T *qi, dict_T *what)
}
/// Return default values for quickfix list properties in retdict.
-static int qf_getprop_defaults(qf_info_T *qi, int flags, dict_T *retdict)
+static int qf_getprop_defaults(qf_info_T *qi,
+ int flags,
+ int locstack,
+ dict_T *retdict)
{
int status = OK;
@@ -5053,7 +5643,7 @@ static int qf_getprop_defaults(qf_info_T *qi, int flags, dict_T *retdict)
if ((status == OK) && (flags & QF_GETLIST_TICK)) {
status = tv_dict_add_nr(retdict, S_LEN("changedtick"), 0);
}
- if ((status == OK) && (qi != &ql_info) && (flags & QF_GETLIST_FILEWINID)) {
+ if ((status == OK) && locstack && (flags & QF_GETLIST_FILEWINID)) {
status = tv_dict_add_nr(retdict, S_LEN("filewinid"), 0);
}
@@ -5061,10 +5651,10 @@ static int qf_getprop_defaults(qf_info_T *qi, int flags, dict_T *retdict)
}
/// Return the quickfix list title as 'title' in retdict
-static int qf_getprop_title(qf_info_T *qi, int qf_idx, dict_T *retdict)
+static int qf_getprop_title(qf_list_T *qfl, dict_T *retdict)
{
return tv_dict_add_str(retdict, S_LEN("title"),
- (const char *)qi->qf_lists[qf_idx].qf_title);
+ (const char *)qfl->qf_title);
}
// Returns the identifier of the window used to display files from a location
@@ -5097,13 +5687,13 @@ static int qf_getprop_items(qf_info_T *qi, int qf_idx, dict_T *retdict)
}
/// Return the quickfix list context (if any) as 'context' in retdict.
-static int qf_getprop_ctx(qf_info_T *qi, int qf_idx, dict_T *retdict)
+static int qf_getprop_ctx(qf_list_T *qfl, dict_T *retdict)
{
int status;
- if (qi->qf_lists[qf_idx].qf_ctx != NULL) {
+ if (qfl->qf_ctx != NULL) {
dictitem_T *di = tv_dict_item_alloc_len(S_LEN("context"));
- tv_copy(qi->qf_lists[qf_idx].qf_ctx, &di->di_tv);
+ tv_copy(qfl->qf_ctx, &di->di_tv);
status = tv_dict_add(retdict, di);
if (status == FAIL) {
tv_dict_item_free(di);
@@ -5115,15 +5705,15 @@ static int qf_getprop_ctx(qf_info_T *qi, int qf_idx, dict_T *retdict)
return status;
}
-/// Return the quickfix list index as 'idx' in retdict
-static int qf_getprop_idx(qf_info_T *qi, int qf_idx, dict_T *retdict)
+/// Return the current quickfix list index as 'idx' in retdict
+static int qf_getprop_idx(qf_list_T *qfl, dict_T *retdict)
{
- int idx = qi->qf_lists[qf_idx].qf_index;
- if (qi->qf_lists[qf_idx].qf_count == 0) {
- // For empty lists, qf_index is set to 1
- idx = 0;
+ int curidx = qfl->qf_index;
+ if (qf_list_empty(qfl)) {
+ // For empty lists, current index is set to 0
+ curidx = 0;
}
- return tv_dict_add_nr(retdict, S_LEN("idx"), idx);
+ return tv_dict_add_nr(retdict, S_LEN("idx"), curidx);
}
/// Return quickfix/location list details (title) as a dictionary.
@@ -5132,9 +5722,10 @@ static int qf_getprop_idx(qf_info_T *qi, int qf_idx, dict_T *retdict)
int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
{
qf_info_T *qi = &ql_info;
+ qf_list_T *qfl;
dictitem_T *di = NULL;
int status = OK;
- int qf_idx;
+ int qf_idx = INVALID_QFIDX;
if ((di = tv_dict_find(what, S_LEN("lines"))) != NULL) {
return qf_get_list_from_lines(what, di, retdict);
@@ -5152,11 +5743,13 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
// List is not present or is empty
if (qf_stack_empty(qi) || qf_idx == INVALID_QFIDX) {
- return qf_getprop_defaults(qi, flags, retdict);
+ return qf_getprop_defaults(qi, flags, wp != NULL, retdict);
}
+ qfl = qf_get_list(qi, qf_idx);
+
if (flags & QF_GETLIST_TITLE) {
- status = qf_getprop_title(qi, qf_idx, retdict);
+ status = qf_getprop_title(qfl, retdict);
}
if ((status == OK) && (flags & QF_GETLIST_NR)) {
status = tv_dict_add_nr(retdict, S_LEN("nr"), qf_idx + 1);
@@ -5168,21 +5761,21 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
status = qf_getprop_items(qi, qf_idx, retdict);
}
if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) {
- status = qf_getprop_ctx(qi, qf_idx, retdict);
+ status = qf_getprop_ctx(qfl, retdict);
}
if ((status == OK) && (flags & QF_GETLIST_ID)) {
- status = tv_dict_add_nr(retdict, S_LEN("id"), qi->qf_lists[qf_idx].qf_id);
+ status = tv_dict_add_nr(retdict, S_LEN("id"), qfl->qf_id);
}
if ((status == OK) && (flags & QF_GETLIST_IDX)) {
- status = qf_getprop_idx(qi, qf_idx, retdict);
+ status = qf_getprop_idx(qfl, retdict);
}
if ((status == OK) && (flags & QF_GETLIST_SIZE)) {
status = tv_dict_add_nr(retdict, S_LEN("size"),
- qi->qf_lists[qf_idx].qf_count);
+ qfl->qf_count);
}
if ((status == OK) && (flags & QF_GETLIST_TICK)) {
status = tv_dict_add_nr(retdict, S_LEN("changedtick"),
- qi->qf_lists[qf_idx].qf_changedtick);
+ qfl->qf_changedtick);
}
if ((status == OK) && (wp != NULL) && (flags & QF_GETLIST_FILEWINID)) {
status = qf_getprop_filewinid(wp, qi, retdict);
@@ -5191,11 +5784,10 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
return status;
}
-// Add a new quickfix entry to list at 'qf_idx' in the stack 'qi' from the
-// items in the dict 'd'.
+/// Add a new quickfix entry to list at 'qf_idx' in the stack 'qi' from the
+/// items in the dict 'd'.
static int qf_add_entry_from_dict(
- qf_info_T *qi,
- int qf_idx,
+ qf_list_T *qfl,
const dict_T *d,
bool first_entry)
FUNC_ATTR_NONNULL_ALL
@@ -5241,17 +5833,16 @@ static int qf_add_entry_from_dict(
valid = tv_dict_get_number(d, "valid");
}
- const int status = qf_add_entry(qi,
- qf_idx,
- NULL, // dir
+ const int status = qf_add_entry(qfl,
+ NULL, // dir
(char_u *)filename,
(char_u *)module,
bufnum,
(char_u *)text,
lnum,
col,
- vcol, // vis_col
- (char_u *)pattern, // search pattern
+ vcol, // vis_col
+ (char_u *)pattern, // search pattern
nr,
(char_u)(type == NULL ? NUL : *type),
valid);
@@ -5269,6 +5860,7 @@ static int qf_add_entry_from_dict(
static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list,
char_u *title, int action)
{
+ qf_list_T *qfl = qf_get_list(qi, qf_idx);
qfline_T *old_last = NULL;
int retval = OK;
@@ -5276,12 +5868,13 @@ 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) {
+ qfl = qf_get_list(qi, qf_idx);
+ } else if (action == 'a' && !qf_list_empty(qfl)) {
// Adding to existing list, use last entry.
- old_last = qi->qf_lists[qf_idx].qf_last;
+ old_last = qfl->qf_last;
} else if (action == 'r') {
- qf_free_items(qi, qf_idx);
- qf_store_title(qi, qf_idx, title);
+ qf_free_items(qfl);
+ qf_store_title(qfl, title);
}
TV_LIST_ITER_CONST(list, li, {
@@ -5294,22 +5887,22 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list,
continue;
}
- retval = qf_add_entry_from_dict(qi, qf_idx, d, li == tv_list_first(list));
- if (retval == FAIL) {
+ retval = qf_add_entry_from_dict(qfl, d, li == tv_list_first(list));
+ if (retval == QF_FAIL) {
break;
}
});
- if (qi->qf_lists[qf_idx].qf_index == 0) {
+ if (qfl->qf_index == 0) {
// no valid entry
- qi->qf_lists[qf_idx].qf_nonevalid = true;
+ qfl->qf_nonevalid = true;
} else {
- qi->qf_lists[qf_idx].qf_nonevalid = false;
+ qfl->qf_nonevalid = false;
}
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) {
- qi->qf_lists[qf_idx].qf_index = 1;
+ qfl->qf_ptr = qfl->qf_start;
+ if (!qf_list_empty(qfl)) {
+ qfl->qf_index = 1;
}
}
@@ -5319,7 +5912,7 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list,
return retval;
}
-// Get the quickfix list index from 'nr' or 'id'
+/// Get the quickfix list index from 'nr' or 'id'
static int qf_setprop_get_qfidx(
const qf_info_T *qi,
const dict_T *what,
@@ -5379,13 +5972,13 @@ static int qf_setprop_title(qf_info_T *qi, int qf_idx, const dict_T *what,
const dictitem_T *di)
FUNC_ATTR_NONNULL_ALL
{
+ qf_list_T *qfl = qf_get_list(qi, qf_idx);
if (di->di_tv.v_type != VAR_STRING) {
return FAIL;
}
- xfree(qi->qf_lists[qf_idx].qf_title);
- qi->qf_lists[qf_idx].qf_title =
- (char_u *)tv_dict_get_string(what, "title", true);
+ xfree(qfl->qf_title);
+ qfl->qf_title = (char_u *)tv_dict_get_string(what, "title", true);
if (qf_idx == qi->qf_curlist) {
qf_update_win_titlevar(qi);
}
@@ -5439,7 +6032,7 @@ static int qf_setprop_items_from_lines(
}
if (action == 'r') {
- qf_free_items(qi, qf_idx);
+ qf_free_items(&qi->qf_lists[qf_idx]);
}
if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, errorformat,
false, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) {
@@ -5450,24 +6043,25 @@ static int qf_setprop_items_from_lines(
}
// Set quickfix list context.
-static int qf_setprop_context(qf_info_T *qi, int qf_idx, dictitem_T *di)
+static int qf_setprop_context(qf_list_T *qfl, dictitem_T *di)
FUNC_ATTR_NONNULL_ALL
{
- tv_free(qi->qf_lists[qf_idx].qf_ctx);
+ tv_free(qfl->qf_ctx);
typval_T *ctx = xcalloc(1, sizeof(typval_T));
tv_copy(&di->di_tv, ctx);
- qi->qf_lists[qf_idx].qf_ctx = ctx;
+ qfl->qf_ctx = ctx;
return OK;
}
-// Set quickfix/location list properties (title, items, context).
-// Also used to add items from parsing a list of lines.
-// Used by the setqflist() and setloclist() Vim script functions.
+/// Set quickfix/location list properties (title, items, context).
+/// Also used to add items from parsing a list of lines.
+/// Used by the setqflist() and setloclist() Vim script functions.
static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action,
char_u *title)
FUNC_ATTR_NONNULL_ALL
{
+ qf_list_T *qfl;
dictitem_T *di;
int retval = FAIL;
bool newlist = action == ' ' || qf_stack_empty(qi);
@@ -5482,6 +6076,7 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action,
qf_idx = qi->qf_curlist;
}
+ qfl = qf_get_list(qi, qf_idx);
if ((di = tv_dict_find(what, S_LEN("title"))) != NULL) {
retval = qf_setprop_title(qi, qf_idx, what, di);
}
@@ -5492,17 +6087,18 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action,
retval = qf_setprop_items_from_lines(qi, qf_idx, what, di, action);
}
if ((di = tv_dict_find(what, S_LEN("context"))) != NULL) {
- retval = qf_setprop_context(qi, qf_idx, di);
+ retval = qf_setprop_context(qfl, di);
}
if (retval == OK) {
- qf_list_changed(qi, qf_idx);
+ qf_list_changed(qfl);
}
return retval;
}
-// Find the non-location list window with the specified location list.
+/// Find the non-location list window with the specified location list stack in
+/// the current tabpage.
static win_T * find_win_with_ll(qf_info_T *qi)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
@@ -5523,7 +6119,7 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi)
if (qfwin != NULL) {
// If the quickfix/location list window is open, then clear it
if (qi->qf_curlist < qi->qf_listcount) {
- qf_free(qi, qi->qf_curlist);
+ qf_free(qf_get_curlist(qi));
}
qf_update_buffer(qi, NULL);
}
@@ -5547,15 +6143,14 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi)
} else if (IS_LL_WINDOW(orig_wp)) {
// If the location list window is open, then create a new empty location
// list
- qf_info_T *new_ll = ll_new_list();
+ qf_info_T *new_ll = qf_alloc_stack(QFLT_LOCATION);
// first free the list reference in the location list window
ll_free_all(&orig_wp->w_llist_ref);
orig_wp->w_llist_ref = new_ll;
if (llwin != NULL) {
- llwin->w_llist = new_ll;
- new_ll->qf_refcount++;
+ win_set_loclist(wp, new_ll);
}
}
}
@@ -5576,15 +6171,22 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title,
if (action == 'f') {
// Free the entire quickfix or location list stack
qf_free_stack(wp, qi);
- } else if (what != NULL) {
+ return OK;
+ }
+
+ incr_quickfix_busy();
+
+ if (what != NULL) {
retval = qf_set_properties(qi, what, action, title);
} else {
retval = qf_add_entries(qi, qi->qf_curlist, list, title, action);
if (retval == OK) {
- qf_list_changed(qi, qi->qf_curlist);
+ qf_list_changed(qf_get_curlist(qi));
}
}
+ decr_quickfix_busy();
+
return retval;
}
@@ -5635,6 +6237,62 @@ bool set_ref_in_quickfix(int copyID)
return abort;
}
+/// Return the autocmd name for the :cbuffer Ex commands
+static char_u * cbuffer_get_auname(cmdidx_T cmdidx)
+{
+ switch (cmdidx) {
+ case CMD_cbuffer: return (char_u *)"cbuffer";
+ case CMD_cgetbuffer: return (char_u *)"cgetbuffer";
+ case CMD_caddbuffer: return (char_u *)"caddbuffer";
+ case CMD_lbuffer: return (char_u *)"lbuffer";
+ case CMD_lgetbuffer: return (char_u *)"lgetbuffer";
+ case CMD_laddbuffer: return (char_u *)"laddbuffer";
+ default: return NULL;
+ }
+}
+
+/// Process and validate the arguments passed to the :cbuffer, :caddbuffer,
+/// :cgetbuffer, :lbuffer, :laddbuffer, :lgetbuffer Ex commands.
+static int cbuffer_process_args(exarg_T *eap,
+ buf_T **bufp,
+ linenr_T *line1,
+ linenr_T *line2)
+{
+ buf_T *buf = NULL;
+
+ if (*eap->arg == NUL)
+ buf = curbuf;
+ else if (*skipwhite(skipdigits(eap->arg)) == NUL)
+ buf = buflist_findnr(atoi((char *)eap->arg));
+
+ if (buf == NULL) {
+ EMSG(_(e_invarg));
+ return FAIL;
+ }
+
+ if (buf->b_ml.ml_mfp == NULL) {
+ EMSG(_("E681: Buffer is not loaded"));
+ return FAIL;
+ }
+
+ if (eap->addr_count == 0) {
+ eap->line1 = 1;
+ eap->line2 = buf->b_ml.ml_line_count;
+ }
+
+ if (eap->line1 < 1 || eap->line1 > buf->b_ml.ml_line_count
+ || eap->line2 < 1 || eap->line2 > buf->b_ml.ml_line_count) {
+ EMSG(_(e_invrange));
+ return FAIL;
+ }
+
+ *line1 = eap->line1;
+ *line2 = eap->line2;
+ *bufp = buf;
+
+ return OK;
+}
+
/*
* ":[range]cbuffer [bufnr]" command.
* ":[range]caddbuffer [bufnr]" command.
@@ -5646,34 +6304,15 @@ bool set_ref_in_quickfix(int copyID)
void ex_cbuffer(exarg_T *eap)
{
buf_T *buf = NULL;
- qf_info_T *qi = &ql_info;
- const char *au_name = NULL;
+ qf_info_T *qi;
+ char_u *au_name = NULL;
win_T *wp = NULL;
+ char_u *qf_title;
+ linenr_T line1;
+ linenr_T line2;
- switch (eap->cmdidx) {
- case CMD_cbuffer:
- au_name = "cbuffer";
- break;
- case CMD_cgetbuffer:
- au_name = "cgetbuffer";
- break;
- case CMD_caddbuffer:
- au_name = "caddbuffer";
- break;
- case CMD_lbuffer:
- au_name = "lbuffer";
- break;
- case CMD_lgetbuffer:
- au_name = "lgetbuffer";
- break;
- case CMD_laddbuffer:
- au_name = "laddbuffer";
- break;
- default:
- break;
- }
-
- if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, (char_u *)au_name,
+ au_name = cbuffer_get_auname(eap->cmdidx);
+ if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
curbuf->b_fname, true, curbuf)) {
if (aborting()) {
return;
@@ -5681,109 +6320,93 @@ void ex_cbuffer(exarg_T *eap)
}
// Must come after autocommands.
- if (is_loclist_cmd(eap->cmdidx)) {
- qi = ll_get_or_alloc_list(curwin);
- wp = curwin;
+ qi = qf_cmd_get_or_alloc_stack(eap, &wp);
+ if (qi == NULL) {
+ return;
}
- if (*eap->arg == NUL)
- buf = curbuf;
- else if (*skipwhite(skipdigits(eap->arg)) == NUL)
- buf = buflist_findnr(atoi((char *)eap->arg));
- if (buf == NULL)
- EMSG(_(e_invarg));
- else if (buf->b_ml.ml_mfp == NULL)
- EMSG(_("E681: Buffer is not loaded"));
- else {
- if (eap->addr_count == 0) {
- eap->line1 = 1;
- eap->line2 = buf->b_ml.ml_line_count;
- }
- if (eap->line1 < 1 || eap->line1 > buf->b_ml.ml_line_count
- || eap->line2 < 1 || eap->line2 > buf->b_ml.ml_line_count) {
- EMSG(_(e_invrange));
- } else {
- char_u *qf_title = qf_cmdtitle(*eap->cmdlinep);
+ if (cbuffer_process_args(eap, &buf, &line1, &line2) == FAIL) {
+ return;
+ }
- if (buf->b_sfname) {
- vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)",
- (char *)qf_title, (char *)buf->b_sfname);
- qf_title = IObuff;
- }
+ qf_title = qf_cmdtitle(*eap->cmdlinep);
- int res = qf_init_ext(qi, qi->qf_curlist, NULL, buf, NULL, p_efm,
- (eap->cmdidx != CMD_caddbuffer
- && eap->cmdidx != CMD_laddbuffer),
- eap->line1, eap->line2, qf_title, NULL);
- if (res >= 0) {
- qf_list_changed(qi, qi->qf_curlist);
- }
- // Remember the current quickfix list identifier, so that we can
- // check for autocommands changing the current quickfix list.
- unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
- if (au_name != NULL) {
- const buf_T *const curbuf_old = curbuf;
- apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name,
- curbuf->b_fname, true, curbuf);
- if (curbuf != curbuf_old) {
- // Autocommands changed buffer, don't jump now, "qi" may
- // be invalid.
- res = 0;
- }
- }
- // Jump to the first error for new list and if autocmds didn't
- // free the list.
- if (res > 0 && (eap->cmdidx == CMD_cbuffer || eap->cmdidx == CMD_lbuffer)
- && qflist_valid(wp, save_qfid)) {
- // display the first error
- qf_jump_first(qi, save_qfid, eap->forceit);
- }
+ if (buf->b_sfname) {
+ vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)",
+ (char *)qf_title, (char *)buf->b_sfname);
+ qf_title = IObuff;
+ }
+
+ incr_quickfix_busy();
+
+ int res = qf_init_ext(qi, qi->qf_curlist, NULL, buf, NULL, p_efm,
+ (eap->cmdidx != CMD_caddbuffer
+ && eap->cmdidx != CMD_laddbuffer),
+ eap->line1, eap->line2, qf_title, NULL);
+ if (qf_stack_empty(qi)) {
+ decr_quickfix_busy();
+ return;
+ }
+ if (res >= 0) {
+ qf_list_changed(qf_get_curlist(qi));
+ }
+ // Remember the current quickfix list identifier, so that we can
+ // check for autocommands changing the current quickfix list.
+ unsigned save_qfid = qf_get_curlist(qi)->qf_id;
+ if (au_name != NULL) {
+ const buf_T *const curbuf_old = curbuf;
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
+ curbuf->b_fname, true, curbuf);
+ if (curbuf != curbuf_old) {
+ // Autocommands changed buffer, don't jump now, "qi" may
+ // be invalid.
+ res = 0;
}
}
+ // Jump to the first error for new list and if autocmds didn't
+ // free the list.
+ if (res > 0 && (eap->cmdidx == CMD_cbuffer || eap->cmdidx == CMD_lbuffer)
+ && qflist_valid(wp, save_qfid)) {
+ // display the first error
+ qf_jump_first(qi, save_qfid, eap->forceit);
+ }
+
+ decr_quickfix_busy();
}
-/*
- * ":cexpr {expr}", ":cgetexpr {expr}", ":caddexpr {expr}" command.
- * ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command.
- */
+/// Return the autocmd name for the :cexpr Ex commands.
+static char_u * cexpr_get_auname(cmdidx_T cmdidx)
+{
+ switch (cmdidx) {
+ case CMD_cexpr: return (char_u *)"cexpr";
+ case CMD_cgetexpr: return (char_u *)"cgetexpr";
+ case CMD_caddexpr: return (char_u *)"caddexpr";
+ case CMD_lexpr: return (char_u *)"lexpr";
+ case CMD_lgetexpr: return (char_u *)"lgetexpr";
+ case CMD_laddexpr: return (char_u *)"laddexpr";
+ default: return NULL;
+ }
+}
+
+/// ":cexpr {expr}", ":cgetexpr {expr}", ":caddexpr {expr}" command.
+/// ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command.
void ex_cexpr(exarg_T *eap)
{
- qf_info_T *qi = &ql_info;
- const char *au_name = NULL;
+ qf_info_T *qi;
+ char_u *au_name = NULL;
win_T *wp = NULL;
- switch (eap->cmdidx) {
- case CMD_cexpr:
- au_name = "cexpr";
- break;
- case CMD_cgetexpr:
- au_name = "cgetexpr";
- break;
- case CMD_caddexpr:
- au_name = "caddexpr";
- break;
- case CMD_lexpr:
- au_name = "lexpr";
- break;
- case CMD_lgetexpr:
- au_name = "lgetexpr";
- break;
- case CMD_laddexpr:
- au_name = "laddexpr";
- break;
- default:
- break;
- }
- if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, (char_u *)au_name,
+ au_name = cexpr_get_auname(eap->cmdidx);
+ if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
curbuf->b_fname, true, curbuf)) {
if (aborting()) {
return;
}
}
- if (is_loclist_cmd(eap->cmdidx)) {
- qi = ll_get_or_alloc_list(curwin);
- wp = curwin;
+ qi = qf_cmd_get_or_alloc_stack(eap, &wp);
+ if (qi == NULL) {
+ return;
}
/* Evaluate the expression. When the result is a string or a list we can
@@ -5792,19 +6415,24 @@ void ex_cexpr(exarg_T *eap)
if (eval0(eap->arg, &tv, NULL, true) != FAIL) {
if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL)
|| tv.v_type == VAR_LIST) {
+ incr_quickfix_busy();
int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, &tv, p_efm,
(eap->cmdidx != CMD_caddexpr
&& eap->cmdidx != CMD_laddexpr),
(linenr_T)0, (linenr_T)0,
qf_cmdtitle(*eap->cmdlinep), NULL);
+ if (qf_stack_empty(qi)) {
+ decr_quickfix_busy();
+ goto cleanup;
+ }
if (res >= 0) {
- qf_list_changed(qi, qi->qf_curlist);
+ qf_list_changed(qf_get_curlist(qi));
}
// Remember the current quickfix list identifier, so that we can
// check for autocommands changing the current quickfix list.
- unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
+ unsigned save_qfid = qf_get_curlist(qi)->qf_id;
if (au_name != NULL) {
- apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name,
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
curbuf->b_fname, true, curbuf);
}
// Jump to the first error for a new list and if autocmds didn't
@@ -5815,9 +6443,11 @@ void ex_cexpr(exarg_T *eap)
// display the first error
qf_jump_first(qi, save_qfid, eap->forceit);
}
+ decr_quickfix_busy();
} else {
EMSG(_("E777: String or List expected"));
}
+cleanup:
tv_clear(&tv);
}
}
@@ -5844,7 +6474,7 @@ static qf_info_T *hgr_get_ll(bool *new_ll)
}
if (qi == NULL) {
// Allocate a new location list for help text matches
- qi = ll_new_list();
+ qi = qf_alloc_stack(QFLT_LOCATION);
*new_ll = true;
}
@@ -5875,8 +6505,7 @@ static void hgr_search_file(
line[--l] = NUL;
}
- if (qf_add_entry(qi,
- qi->qf_curlist,
+ if (qf_add_entry(qf_get_curlist(qi),
NULL, // dir
fname,
NULL,
@@ -5888,8 +6517,8 @@ static void hgr_search_file(
NULL, // search pattern
0, // nr
1, // type
- true // valid
- ) == FAIL) {
+ true) // valid
+ == QF_FAIL) {
got_int = true;
if (line != IObuff) {
xfree(line);
@@ -5984,6 +6613,8 @@ void ex_helpgrep(exarg_T *eap)
qi = hgr_get_ll(&new_qi);
}
+ incr_quickfix_busy();
+
// Check for a specified language
char_u *const lang = check_help_lang(eap->arg);
regmatch_T regmatch = {
@@ -5998,10 +6629,12 @@ void ex_helpgrep(exarg_T *eap)
vim_regfree(regmatch.regprog);
- 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;
- qi->qf_lists[qi->qf_curlist].qf_index = 1;
+ qf_list_T *qfl = qf_get_curlist(qi);
+ qfl->qf_nonevalid = false;
+ qfl->qf_ptr = qfl->qf_start;
+ qfl->qf_index = 1;
+ qf_list_changed(qfl);
+ qf_update_buffer(qi, NULL);
}
if (p_cpo == empty_option) {
@@ -6011,23 +6644,24 @@ void ex_helpgrep(exarg_T *eap)
free_string_option(save_cpo);
}
- qf_list_changed(qi, qi->qf_curlist);
- qf_update_buffer(qi, NULL);
-
if (au_name != NULL) {
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
curbuf->b_fname, true, curbuf);
if (!new_qi && IS_LL_STACK(qi) && qf_find_buf(qi) == NULL) {
// autocommands made "qi" invalid
+ decr_quickfix_busy();
return;
}
}
- /* 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(qf_get_curlist(qi))) {
+ qf_jump(qi, 0, 0, false);
+ } else {
EMSG2(_(e_nomatch2), eap->arg);
+ }
+
+ decr_quickfix_busy();
if (eap->cmdidx == CMD_lhelpgrep) {
// If the help window is not opened or if it already points to the
@@ -6042,3 +6676,4 @@ void ex_helpgrep(exarg_T *eap)
}
}
+
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 31b0c0cd2c..8949b3d968 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -37,6 +37,8 @@ func s:setup_commands(cchar)
command! -nargs=* Xgrepadd <mods> grepadd <args>
command! -nargs=* Xhelpgrep helpgrep <args>
command! -nargs=0 -count Xcc <count>cc
+ command! -count=1 -nargs=0 Xbelow <mods><count>cbelow
+ command! -count=1 -nargs=0 Xabove <mods><count>cabove
let g:Xgetlist = function('getqflist')
let g:Xsetlist = function('setqflist')
call setqflist([], 'f')
@@ -70,6 +72,8 @@ func s:setup_commands(cchar)
command! -nargs=* Xgrepadd <mods> lgrepadd <args>
command! -nargs=* Xhelpgrep lhelpgrep <args>
command! -nargs=0 -count Xcc <count>ll
+ command! -count=1 -nargs=0 Xbelow <mods><count>lbelow
+ command! -count=1 -nargs=0 Xabove <mods><count>labove
let g:Xgetlist = function('getloclist', [0])
let g:Xsetlist = function('setloclist', [0])
call setloclist(0, [], 'f')
@@ -163,6 +167,12 @@ endfunc
func XageTests(cchar)
call s:setup_commands(a:cchar)
+ if a:cchar == 'l'
+ " No location list for the current window
+ call assert_fails('lolder', 'E776:')
+ call assert_fails('lnewer', 'E776:')
+ endif
+
let list = [{'bufnr': bufnr('%'), 'lnum': 1}]
call g:Xsetlist(list)
@@ -561,6 +571,8 @@ func s:test_xhelpgrep(cchar)
" Search for non existing help string
call assert_fails('Xhelpgrep a1b2c3', 'E480:')
+ " Invalid regular expression
+ call assert_fails('Xhelpgrep \@<!', 'E480:')
endfunc
func Test_helpgrep()
@@ -1066,8 +1078,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)
@@ -3358,7 +3370,28 @@ func Test_lexpr_crash()
augroup QF_Test
au!
augroup END
+
+ enew | only
+ augroup QF_Test
+ au!
+ au BufNew * call setloclist(0, [], 'f')
+ augroup END
+ lexpr 'x:1:x'
+ augroup QF_Test
+ au!
+ augroup END
+
enew | only
+ lexpr ''
+ lopen
+ augroup QF_Test
+ au!
+ au FileType * call setloclist(0, [], 'f')
+ augroup END
+ lexpr ''
+ augroup QF_Test
+ au!
+ augroup END
endfunc
" The following test used to crash Vim
@@ -3809,4 +3842,110 @@ func Test_viscol()
call delete('Xfile1')
endfunc
+" Test for the :cbelow, :cabove, :lbelow and :labove commands.
+func Xtest_below(cchar)
+ call s:setup_commands(a:cchar)
+
+ " No quickfix/location list
+ call assert_fails('Xbelow', 'E42:')
+ call assert_fails('Xabove', 'E42:')
+
+ " Empty quickfix/location list
+ call g:Xsetlist([])
+ call assert_fails('Xbelow', 'E42:')
+ call assert_fails('Xabove', 'E42:')
+
+ call s:create_test_file('X1')
+ call s:create_test_file('X2')
+ call s:create_test_file('X3')
+ call s:create_test_file('X4')
+
+ " Invalid entries
+ edit X1
+ call g:Xsetlist(["E1", "E2"])
+ call assert_fails('Xbelow', 'E42:')
+ call assert_fails('Xabove', 'E42:')
+ call assert_fails('3Xbelow', 'E42:')
+ call assert_fails('4Xabove', 'E42:')
+
+ " Test the commands with various arguments
+ Xexpr ["X1:5:L5", "X2:5:L5", "X2:10:L10", "X2:15:L15", "X3:3:L3"]
+ edit +7 X2
+ Xabove
+ call assert_equal(['X2', 5], [bufname(''), line('.')])
+ call assert_fails('Xabove', 'E553:')
+ normal 2j
+ Xbelow
+ call assert_equal(['X2', 10], [bufname(''), line('.')])
+ " Last error in this file
+ Xbelow 99
+ call assert_equal(['X2', 15], [bufname(''), line('.')])
+ call assert_fails('Xbelow', 'E553:')
+ " First error in this file
+ Xabove 99
+ call assert_equal(['X2', 5], [bufname(''), line('.')])
+ call assert_fails('Xabove', 'E553:')
+ normal gg
+ Xbelow 2
+ call assert_equal(['X2', 10], [bufname(''), line('.')])
+ normal G
+ Xabove 2
+ call assert_equal(['X2', 10], [bufname(''), line('.')])
+ edit X4
+ call assert_fails('Xabove', 'E42:')
+ call assert_fails('Xbelow', 'E42:')
+ if a:cchar == 'l'
+ " If a buffer has location list entries from some other window but not
+ " from the current window, then the commands should fail.
+ edit X1 | split | call setloclist(0, [], 'f')
+ call assert_fails('Xabove', 'E776:')
+ call assert_fails('Xbelow', 'E776:')
+ close
+ endif
+
+ " Test for lines with multiple quickfix entries
+ Xexpr ["X1:5:L5", "X2:5:1:L5_1", "X2:5:2:L5_2", "X2:5:3:L5_3",
+ \ "X2:10:1:L10_1", "X2:10:2:L10_2", "X2:10:3:L10_3",
+ \ "X2:15:1:L15_1", "X2:15:2:L15_2", "X2:15:3:L15_3", "X3:3:L3"]
+ edit +1 X2
+ Xbelow 2
+ call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')])
+ normal gg
+ Xbelow 99
+ call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')])
+ normal G
+ Xabove 2
+ call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')])
+ normal G
+ Xabove 99
+ call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
+ normal 10G
+ Xabove
+ call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
+ normal 10G
+ Xbelow
+ call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')])
+
+ " Invalid range
+ if a:cchar == 'c'
+ call assert_fails('-2cbelow', 'E553:')
+ " TODO: should go to first error in the current line?
+ 0cabove
+ else
+ call assert_fails('-2lbelow', 'E553:')
+ " TODO: should go to first error in the current line?
+ 0labove
+ endif
+
+ call delete('X1')
+ call delete('X2')
+ call delete('X3')
+ call delete('X4')
+endfunc
+
+func Test_cbelow()
+ call Xtest_below('c')
+ call Xtest_below('l')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab