patch 8.1.1256: cannot navigate through errors relative to the cursor

Problem:    Cannot navigate through errors relative to the cursor.
Solution:   Add :cabove, :cbelow, :labove and :lbelow. (Yegappan Lakshmanan,
            closes #4316)
This commit is contained in:
Bram Moolenaar
2019-05-03 21:56:35 +02:00
parent 12e91862c1
commit 3ff33114d7
9 changed files with 494 additions and 29 deletions

View File

@ -1188,11 +1188,13 @@ tag command action ~
|:cNfile| :cNf[ile] go to last error in previous file |:cNfile| :cNf[ile] go to last error in previous file
|:cabbrev| :ca[bbrev] like ":abbreviate" but for Command-line mode |:cabbrev| :ca[bbrev] like ":abbreviate" but for Command-line mode
|:cabclear| :cabc[lear] clear all abbreviations for Command-line mode |:cabclear| :cabc[lear] clear all abbreviations for Command-line mode
|:cabove| :cabo[ve] go to error above current line
|:caddbuffer| :cad[dbuffer] add errors from buffer |:caddbuffer| :cad[dbuffer] add errors from buffer
|:caddexpr| :cadde[xpr] add errors from expr |:caddexpr| :cadde[xpr] add errors from expr
|:caddfile| :caddf[ile] add error message to current quickfix list |:caddfile| :caddf[ile] add error message to current quickfix list
|:call| :cal[l] call a function |:call| :cal[l] call a function
|:catch| :cat[ch] part of a :try command |:catch| :cat[ch] part of a :try command
|:cbelow| :cbe[low] got to error below current line
|:cbottom| :cbo[ttom] scroll to the bottom of the quickfix window |:cbottom| :cbo[ttom] scroll to the bottom of the quickfix window
|:cbuffer| :cb[uffer] parse error messages and jump to first error |:cbuffer| :cb[uffer] parse error messages and jump to first error
|:cc| :cc go to specific error |:cc| :cc go to specific error
@ -1350,12 +1352,14 @@ tag command action ~
|:lNext| :lN[ext] go to previous entry in location list |:lNext| :lN[ext] go to previous entry in location list
|:lNfile| :lNf[ile] go to last entry in previous file |:lNfile| :lNf[ile] go to last entry in previous file
|:list| :l[ist] print lines |:list| :l[ist] print lines
|:labove| :lab[ove] go to location above current line
|:laddexpr| :lad[dexpr] add locations from expr |:laddexpr| :lad[dexpr] add locations from expr
|:laddbuffer| :laddb[uffer] add locations from buffer |:laddbuffer| :laddb[uffer] add locations from buffer
|:laddfile| :laddf[ile] add locations to current location list |:laddfile| :laddf[ile] add locations to current location list
|:last| :la[st] go to the last file in the argument list |:last| :la[st] go to the last file in the argument list
|:language| :lan[guage] set the language (locale) |:language| :lan[guage] set the language (locale)
|:later| :lat[er] go to newer change, redo |:later| :lat[er] go to newer change, redo
|:lbelow| :lbe[low] go to location below current line
|:lbottom| :lbo[ttom] scroll to the bottom of the location window |:lbottom| :lbo[ttom] scroll to the bottom of the location window
|:lbuffer| :lb[uffer] parse locations and jump to first location |:lbuffer| :lb[uffer] parse locations and jump to first location
|:lcd| :lc[d] change directory locally |:lcd| :lc[d] change directory locally

View File

@ -123,6 +123,36 @@ processing a quickfix or location list command, it will be aborted.
list for the current window is used instead of the list for the current window is used instead of the
quickfix list. quickfix list.
*:cabo* *:cabove*
:[count]cabo[ve] Go to the [count] error above the current line in the
current buffer. If [count] is omitted, then 1 is
used. If there are no errors, then an error message
is displayed. Assumes that the entries in a quickfix
list are sorted by their buffer number and line
number. If there are multiple errors on the same line,
then only the first entry is used. If [count] exceeds
the number of entries above the current line, then the
first error in the file is selected.
*:lab* *:labove*
:[count]lab[ove] Same as ":cabove", except the location list for the
current window is used instead of the quickfix list.
*:cbe* *:cbelow*
:[count]cbe[low] Go to the [count] error below the current line in the
current buffer. If [count] is omitted, then 1 is
used. If there are no errors, then an error message
is displayed. Assumes that the entries in a quickfix
list are sorted by their buffer number and line
number. If there are multiple errors on the same
line, then only the first entry is used. If [count]
exceeds the number of entries below the current line,
then the last error in the file is selected.
*:lbe* *:lbelow*
:[count]lbe[low] Same as ":cbelow", except the location list for the
current window is used instead of the quickfix list.
*:cnf* *:cnfile* *:cnf* *:cnfile*
:[count]cnf[ile][!] Display the first error in the [count] next file in :[count]cnf[ile][!] Display the first error in the [count] next file in
the list that includes a file name. If there are no the list that includes a file name. If there are no

View File

@ -8,29 +8,29 @@ static const unsigned short cmdidxs1[26] =
/* a */ 0, /* a */ 0,
/* b */ 19, /* b */ 19,
/* c */ 42, /* c */ 42,
/* d */ 103, /* d */ 105,
/* e */ 125, /* e */ 127,
/* f */ 145, /* f */ 147,
/* g */ 161, /* g */ 163,
/* h */ 167, /* h */ 169,
/* i */ 176, /* i */ 178,
/* j */ 194, /* j */ 196,
/* k */ 196, /* k */ 198,
/* l */ 201, /* l */ 203,
/* m */ 259, /* m */ 263,
/* n */ 277, /* n */ 281,
/* o */ 297, /* o */ 301,
/* p */ 309, /* p */ 313,
/* q */ 348, /* q */ 352,
/* r */ 351, /* r */ 355,
/* s */ 371, /* s */ 375,
/* t */ 439, /* t */ 443,
/* u */ 484, /* u */ 488,
/* v */ 495, /* v */ 499,
/* w */ 513, /* w */ 517,
/* x */ 527, /* x */ 531,
/* y */ 536, /* y */ 540,
/* z */ 537 /* z */ 541
}; };
/* /*
@ -43,7 +43,7 @@ static const unsigned char cmdidxs2[26][26] =
{ /* a b c d e f g h i j k l m n o p q r s t u v w x y z */ { /* a b c d e f g h i j k l m n o p q r s t u v w x y z */
/* a */ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 0, 0, 0, 7, 15, 0, 16, 0, 0, 0, 0, 0 }, /* a */ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 0, 0, 0, 7, 15, 0, 16, 0, 0, 0, 0, 0 },
/* b */ { 2, 0, 0, 4, 5, 7, 0, 0, 0, 0, 0, 8, 9, 10, 11, 12, 0, 13, 0, 0, 0, 0, 22, 0, 0, 0 }, /* b */ { 2, 0, 0, 4, 5, 7, 0, 0, 0, 0, 0, 8, 9, 10, 11, 12, 0, 13, 0, 0, 0, 0, 22, 0, 0, 0 },
/* c */ { 3, 10, 12, 14, 16, 18, 21, 0, 0, 0, 0, 29, 33, 36, 42, 51, 53, 54, 55, 0, 57, 0, 60, 0, 0, 0 }, /* c */ { 3, 11, 14, 16, 18, 20, 23, 0, 0, 0, 0, 31, 35, 38, 44, 53, 55, 56, 57, 0, 59, 0, 62, 0, 0, 0 },
/* d */ { 0, 0, 0, 0, 0, 0, 0, 0, 6, 15, 0, 16, 0, 0, 17, 0, 0, 19, 20, 0, 0, 0, 0, 0, 0, 0 }, /* d */ { 0, 0, 0, 0, 0, 0, 0, 0, 6, 15, 0, 16, 0, 0, 17, 0, 0, 19, 20, 0, 0, 0, 0, 0, 0, 0 },
/* e */ { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0 }, /* e */ { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0 },
/* f */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0 }, /* f */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0 },
@ -52,7 +52,7 @@ static const unsigned char cmdidxs2[26][26] =
/* i */ { 1, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, 5, 6, 0, 0, 0, 0, 0, 13, 0, 15, 0, 0, 0, 0, 0 }, /* i */ { 1, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, 5, 6, 0, 0, 0, 0, 0, 13, 0, 15, 0, 0, 0, 0, 0 },
/* j */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, /* j */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
/* k */ { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* k */ { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
/* l */ { 3, 9, 11, 15, 16, 20, 23, 28, 0, 0, 0, 30, 33, 36, 40, 46, 0, 48, 57, 49, 50, 54, 56, 0, 0, 0 }, /* l */ { 3, 10, 13, 17, 18, 22, 25, 30, 0, 0, 0, 32, 35, 38, 42, 48, 0, 50, 59, 51, 52, 56, 58, 0, 0, 0 },
/* m */ { 1, 0, 0, 0, 7, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 }, /* m */ { 1, 0, 0, 0, 7, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 },
/* n */ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 10, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0 }, /* n */ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 10, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0 },
/* o */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 5, 0, 0, 0, 0, 0, 0, 9, 0, 11, 0, 0, 0 }, /* o */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 5, 0, 0, 0, 0, 0, 0, 9, 0, 11, 0, 0, 0 },
@ -69,4 +69,4 @@ static const unsigned char cmdidxs2[26][26] =
/* z */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } /* z */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
}; };
static const int command_count = 550; static const int command_count = 554;

View File

@ -252,6 +252,9 @@ EX(CMD_cabbrev, "cabbrev", ex_abbreviate,
EX(CMD_cabclear, "cabclear", ex_abclear, EX(CMD_cabclear, "cabclear", ex_abclear,
EXTRA|TRLBAR|CMDWIN, EXTRA|TRLBAR|CMDWIN,
ADDR_NONE), ADDR_NONE),
EX(CMD_cabove, "cabove", ex_cbelow,
RANGE|TRLBAR,
ADDR_OTHER),
EX(CMD_caddbuffer, "caddbuffer", ex_cbuffer, EX(CMD_caddbuffer, "caddbuffer", ex_cbuffer,
RANGE|WORD1|TRLBAR, RANGE|WORD1|TRLBAR,
ADDR_OTHER), ADDR_OTHER),
@ -270,6 +273,9 @@ EX(CMD_catch, "catch", ex_catch,
EX(CMD_cbuffer, "cbuffer", ex_cbuffer, EX(CMD_cbuffer, "cbuffer", ex_cbuffer,
BANG|RANGE|WORD1|TRLBAR, BANG|RANGE|WORD1|TRLBAR,
ADDR_OTHER), ADDR_OTHER),
EX(CMD_cbelow, "cbelow", ex_cbelow,
RANGE|TRLBAR,
ADDR_OTHER),
EX(CMD_cbottom, "cbottom", ex_cbottom, EX(CMD_cbottom, "cbottom", ex_cbottom,
TRLBAR, TRLBAR,
ADDR_NONE), ADDR_NONE),
@ -726,6 +732,9 @@ EX(CMD_lNfile, "lNfile", ex_cnext,
EX(CMD_last, "last", ex_last, EX(CMD_last, "last", ex_last,
EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR, EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR,
ADDR_NONE), ADDR_NONE),
EX(CMD_labove, "labove", ex_cbelow,
RANGE|TRLBAR,
ADDR_OTHER),
EX(CMD_language, "language", ex_language, EX(CMD_language, "language", ex_language,
EXTRA|TRLBAR|CMDWIN, EXTRA|TRLBAR|CMDWIN,
ADDR_NONE), ADDR_NONE),
@ -744,6 +753,9 @@ EX(CMD_later, "later", ex_later,
EX(CMD_lbuffer, "lbuffer", ex_cbuffer, EX(CMD_lbuffer, "lbuffer", ex_cbuffer,
BANG|RANGE|WORD1|TRLBAR, BANG|RANGE|WORD1|TRLBAR,
ADDR_OTHER), ADDR_OTHER),
EX(CMD_lbelow, "lbelow", ex_cbelow,
RANGE|TRLBAR,
ADDR_OTHER),
EX(CMD_lbottom, "lbottom", ex_cbottom, EX(CMD_lbottom, "lbottom", ex_cbottom,
TRLBAR, TRLBAR,
ADDR_NONE), ADDR_NONE),

View File

@ -56,6 +56,7 @@ static int getargopt(exarg_T *eap);
# define ex_cbuffer ex_ni # define ex_cbuffer ex_ni
# define ex_cc ex_ni # define ex_cc ex_ni
# define ex_cnext ex_ni # define ex_cnext ex_ni
# define ex_cbelow ex_ni
# define ex_cfile ex_ni # define ex_cfile ex_ni
# define qf_list ex_ni # define qf_list ex_ni
# define qf_age ex_ni # define qf_age ex_ni

View File

@ -23,6 +23,7 @@ int qf_get_cur_idx(exarg_T *eap);
int qf_get_cur_valid_idx(exarg_T *eap); int qf_get_cur_valid_idx(exarg_T *eap);
void ex_cc(exarg_T *eap); void ex_cc(exarg_T *eap);
void ex_cnext(exarg_T *eap); void ex_cnext(exarg_T *eap);
void ex_cbelow(exarg_T *eap);
void ex_cfile(exarg_T *eap); void ex_cfile(exarg_T *eap);
void ex_vimgrep(exarg_T *eap); void ex_vimgrep(exarg_T *eap);
int get_errorlist(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);

View File

@ -177,6 +177,7 @@ static buf_T *load_dummy_buffer(char_u *fname, char_u *dirname_start, char_u *re
static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start); static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start);
static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start); static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start);
static qf_info_T *ll_get_or_alloc_list(win_T *); static qf_info_T *ll_get_or_alloc_list(win_T *);
static char_u *e_no_more_items = (char_u *)N_("E553: No more items");
// Quickfix window check helper macro // Quickfix window check helper macro
#define IS_QF_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref == NULL) #define IS_QF_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref == NULL)
@ -1493,6 +1494,16 @@ qf_list_empty(qf_list_T *qfl)
return qfl == NULL || qfl->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 int
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 * Return a pointer to a list in the specified quickfix stack
*/ */
@ -2700,7 +2711,6 @@ get_nth_valid_entry(
int qf_idx = qfl->qf_index; int qf_idx = qfl->qf_index;
qfline_T *prev_qf_ptr; qfline_T *prev_qf_ptr;
int prev_index; int prev_index;
static char_u *e_no_more_items = (char_u *)N_("E553: No more items");
char_u *err = e_no_more_items; char_u *err = e_no_more_items;
while (errornr--) while (errornr--)
@ -4886,7 +4896,7 @@ qf_get_cur_valid_idx(exarg_T *eap)
qfp = qfl->qf_start; qfp = qfl->qf_start;
// check if the list has valid errors // check if the list has valid errors
if (qfl->qf_count <= 0 || qfl->qf_nonevalid) if (!qf_list_has_valid_entries(qfl))
return 1; return 1;
for (i = 1; i <= qfl->qf_index && qfp!= NULL; i++, qfp = qfp->qf_next) for (i = 1; i <= qfl->qf_index && qfp!= NULL; i++, qfp = qfp->qf_next)
@ -4924,7 +4934,7 @@ qf_get_nth_valid_entry(qf_list_T *qfl, int n, int fdo)
int prev_fnum = 0; int prev_fnum = 0;
// check if the list has valid errors // check if the list has valid errors
if (qfl->qf_count <= 0 || qfl->qf_nonevalid) if (!qf_list_has_valid_entries(qfl))
return 1; return 1;
eidx = 0; eidx = 0;
@ -5044,6 +5054,301 @@ ex_cnext(exarg_T *eap)
qf_jump(qi, dir, errornr, eap->forceit); 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, int 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, int 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, int 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 * Return the autocmd name for the :cfile Ex commands
*/ */

View File

@ -37,6 +37,8 @@ func s:setup_commands(cchar)
command! -nargs=* Xgrepadd <mods> grepadd <args> command! -nargs=* Xgrepadd <mods> grepadd <args>
command! -nargs=* Xhelpgrep helpgrep <args> command! -nargs=* Xhelpgrep helpgrep <args>
command! -nargs=0 -count Xcc <count>cc 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:Xgetlist = function('getqflist')
let g:Xsetlist = function('setqflist') let g:Xsetlist = function('setqflist')
call setqflist([], 'f') call setqflist([], 'f')
@ -70,6 +72,8 @@ func s:setup_commands(cchar)
command! -nargs=* Xgrepadd <mods> lgrepadd <args> command! -nargs=* Xgrepadd <mods> lgrepadd <args>
command! -nargs=* Xhelpgrep lhelpgrep <args> command! -nargs=* Xhelpgrep lhelpgrep <args>
command! -nargs=0 -count Xcc <count>ll 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:Xgetlist = function('getloclist', [0])
let g:Xsetlist = function('setloclist', [0]) let g:Xsetlist = function('setloclist', [0])
call setloclist(0, [], 'f') call setloclist(0, [], 'f')
@ -4035,3 +4039,109 @@ func Test_empty_qfbuf()
enew enew
call delete('Xfile1') call delete('Xfile1')
endfunc 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

View File

@ -767,6 +767,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1256,
/**/ /**/
1255, 1255,
/**/ /**/