vim-patch:8.1.1275: cannot navigate to errors before/after the cursor

Problem:    Cannot navigate to errors before/after the cursor.
Solution:   Add the :cbefore and :cafter commands. (Yegappan Lakshmanan,
            closes vim/vim#4340)
cf6a55c4b0
This commit is contained in:
Jan Edmund Lazo 2021-01-01 03:32:43 -05:00
parent 5cf94effee
commit d1608f7503
No known key found for this signature in database
GPG Key ID: 64915E6E9F735B15
5 changed files with 306 additions and 76 deletions

View File

@ -1173,9 +1173,11 @@ tag command action ~
|: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
|:cafter| :caf[ter] go to error after current cursor
|: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] go to error below current line |:cbefore| :cbef[ore] go to error before current cursor
|:cbelow| :cbel[ow] go 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
@ -1336,10 +1338,12 @@ tag command action ~
|: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
|:lafter| :laf[ter] go to location after current cursor
|: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 |:lbefore| :lbef[ore] go to location before current cursor
|:lbelow| :lbel[ow] 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

@ -128,8 +128,8 @@ processing a quickfix or location list command, it will be aborted.
:[count]lab[ove] Same as ":cabove", except the location list for the :[count]lab[ove] Same as ":cabove", except the location list for the
current window is used instead of the quickfix list. current window is used instead of the quickfix list.
*:cbe* *:cbelow* *:cbel* *:cbelow*
:[count]cbe[low] Go to the [count] error below the current line in the :[count]cbel[ow] Go to the [count] error below the current line in the
current buffer. If [count] is omitted, then 1 is current buffer. If [count] is omitted, then 1 is
used. If there are no errors, then an error message used. If there are no errors, then an error message
is displayed. Assumes that the entries in a quickfix is displayed. Assumes that the entries in a quickfix
@ -139,8 +139,36 @@ processing a quickfix or location list command, it will be aborted.
exceeds the number of entries below the current line, exceeds the number of entries below the current line,
then the last error in the file is selected. then the last error in the file is selected.
*:lbe* *:lbelow* *:lbel* *:lbelow*
:[count]lbe[low] Same as ":cbelow", except the location list for the :[count]lbel[ow] Same as ":cbelow", except the location list for the
current window is used instead of the quickfix list.
*:cbe* *:cbefore*
:[count]cbe[fore] Go to the [count] error before the current cursor
position 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,
line and column numbers. If [count] exceeds the
number of entries before the current position, then
the first error in the file is selected.
*:lbe* *:lbefore*
:[count]lbe[fore] Same as ":cbefore", except the location list for the
current window is used instead of the quickfix list.
*:caf* *:cafter*
:[count]caf[ter] Go to the [count] error after the current cursor
position 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,
line and column numbers. If [count] exceeds the
number of entries after the current position, then
the last error in the file is selected.
*:laf* *:lafter*
:[count]laf[ter] Same as ":cafter", except the location list for the
current window is used instead of the quickfix list. current window is used instead of the quickfix list.
*:cnf* *:cnfile* *:cnf* *:cnfile*

View File

@ -342,6 +342,12 @@ module.cmds = {
addr_type='ADDR_NONE', addr_type='ADDR_NONE',
func='ex_cfile', func='ex_cfile',
}, },
{
command='cafter',
flags=bit.bor(RANGE, COUNT, TRLBAR),
addr_type='ADDR_UNSIGNED',
func='ex_cbelow',
},
{ {
command='call', command='call',
flags=bit.bor(RANGE, NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN), flags=bit.bor(RANGE, NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
@ -360,6 +366,12 @@ module.cmds = {
addr_type='ADDR_OTHER', addr_type='ADDR_OTHER',
func='ex_cbuffer', func='ex_cbuffer',
}, },
{
command='cbefore',
flags=bit.bor(RANGE, COUNT, TRLBAR),
addr_type='ADDR_UNSIGNED',
func='ex_cbelow',
},
{ {
command='cbelow', command='cbelow',
flags=bit.bor(RANGE, COUNT, TRLBAR), flags=bit.bor(RANGE, COUNT, TRLBAR),
@ -1310,6 +1322,12 @@ module.cmds = {
addr_type='ADDR_NONE', addr_type='ADDR_NONE',
func='ex_cfile', func='ex_cfile',
}, },
{
command='lafter',
flags=bit.bor(RANGE, COUNT, TRLBAR),
addr_type='ADDR_UNSIGNED',
func='ex_cbelow',
},
{ {
command='later', command='later',
flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN), flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN),
@ -1322,6 +1340,12 @@ module.cmds = {
addr_type='ADDR_OTHER', addr_type='ADDR_OTHER',
func='ex_cbuffer', func='ex_cbuffer',
}, },
{
command='lbefore',
flags=bit.bor(RANGE, COUNT, TRLBAR),
addr_type='ADDR_UNSIGNED',
func='ex_cbelow',
},
{ {
command='lbelow', command='lbelow',
flags=bit.bor(RANGE, COUNT, TRLBAR), flags=bit.bor(RANGE, COUNT, TRLBAR),

View File

@ -4573,74 +4573,150 @@ static qfline_T * qf_find_last_entry_on_line(qfline_T *entry, int *errornr)
return entry; return entry;
} }
/// Find the first quickfix entry below line 'lnum' in buffer 'bnr'. // Returns true if the specified quickfix entry is
// after the given line (linewise is true)
// or after the line and column.
static bool qf_entry_after_pos(const qfline_T *qfp, const pos_T *pos,
bool linewise)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (linewise) {
return qfp->qf_lnum > pos->lnum;
}
return qfp->qf_lnum > pos->lnum
|| (qfp->qf_lnum == pos->lnum && qfp->qf_col > pos->col);
}
// Returns true if the specified quickfix entry is
// before the given line (linewise is true)
// or before the line and column.
static bool qf_entry_before_pos(const qfline_T *qfp, const pos_T *pos,
bool linewise)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (linewise) {
return qfp->qf_lnum < pos->lnum;
}
return qfp->qf_lnum < pos->lnum
|| (qfp->qf_lnum == pos->lnum && qfp->qf_col < pos->col);
}
// Returns true if the specified quickfix entry is
// on or after the given line (linewise is true)
// or on or after the line and column.
static bool qf_entry_on_or_after_pos(const qfline_T *qfp, const pos_T *pos,
bool linewise)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (linewise) {
return qfp->qf_lnum >= pos->lnum;
}
return qfp->qf_lnum > pos->lnum
|| (qfp->qf_lnum == pos->lnum && qfp->qf_col >= pos->col);
}
// Returns true if the specified quickfix entry is
// on or before the given line (linewise is true)
// or on or before the line and column.
static bool qf_entry_on_or_before_pos(const qfline_T *qfp, const pos_T *pos,
bool linewise)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (linewise) {
return qfp->qf_lnum <= pos->lnum;
}
return qfp->qf_lnum < pos->lnum
|| (qfp->qf_lnum == pos->lnum && qfp->qf_col <= pos->col);
}
/// Find the first quickfix entry after position 'pos' in buffer 'bnr'.
/// If 'linewise' is true, returns the entry after the specified line and treats
/// multiple entries on a single line as one. Otherwise returns the entry after
/// the specified line and column.
/// 'qfp' points to the very first entry in the buffer and 'errornr' is the /// 'qfp' points to the very first entry in the buffer and 'errornr' is the
/// index of the very first entry in the quickfix list. /// index of the very first entry in the quickfix list.
/// Returns NULL if an entry is not found after 'lnum'. /// Returns NULL if an entry is not found after 'pos'.
static qfline_T *qf_find_entry_on_next_line(int bnr, static qfline_T *qf_find_entry_after_pos(
linenr_T lnum, int bnr,
const pos_T *pos,
bool linewise,
qfline_T *qfp, qfline_T *qfp,
int *errornr) int *errornr
)
FUNC_ATTR_NONNULL_ALL
{ {
if (qfp->qf_lnum > lnum) { if (qf_entry_after_pos(qfp, pos, linewise)) {
// First entry is after line 'lnum' // First entry is after postion 'pos'
return qfp; return qfp;
} }
// Find the entry just before or at the line 'lnum' // Find the entry just before or at the position 'pos'
while (qfp->qf_next != NULL while (qfp->qf_next != NULL
&& qfp->qf_next->qf_fnum == bnr && qfp->qf_next->qf_fnum == bnr
&& qfp->qf_next->qf_lnum <= lnum) { && qf_entry_on_or_before_pos(qfp->qf_next, pos, linewise)) {
qfp = qfp->qf_next; qfp = qfp->qf_next;
(*errornr)++; (*errornr)++;
} }
if (qfp->qf_next == NULL || qfp->qf_next->qf_fnum != bnr) { if (qfp->qf_next == NULL || qfp->qf_next->qf_fnum != bnr) {
// No entries found after 'lnum' // No entries found after position 'pos'
return NULL; return NULL;
} }
// Use the entry just after line 'lnum' // Use the entry just after position 'pos'
qfp = qfp->qf_next; qfp = qfp->qf_next;
(*errornr)++; (*errornr)++;
return qfp; return qfp;
} }
/// Find the first quickfix entry before line 'lnum' in buffer 'bnr'. /// Find the first quickfix entry before position 'pos' in buffer 'bnr'.
/// If 'linewise' is true, returns the entry before the specified line and
/// treats multiple entries on a single line as one. Otherwise returns the entry
/// before the specified line and column.
/// 'qfp' points to the very first entry in the buffer and 'errornr' is the /// 'qfp' points to the very first entry in the buffer and 'errornr' is the
/// index of the very first entry in the quickfix list. /// index of the very first entry in the quickfix list.
/// Returns NULL if an entry is not found before 'lnum'. /// Returns NULL if an entry is not found before 'pos'.
static qfline_T *qf_find_entry_on_prev_line(int bnr, static qfline_T *qf_find_entry_before_pos(
linenr_T lnum, int bnr,
const pos_T *pos,
bool linewise,
qfline_T *qfp, qfline_T *qfp,
int *errornr) int *errornr
)
FUNC_ATTR_NONNULL_ALL
{ {
// Find the entry just before the line 'lnum' // Find the entry just before the position 'pos'
while (qfp->qf_next != NULL while (qfp->qf_next != NULL
&& qfp->qf_next->qf_fnum == bnr && qfp->qf_next->qf_fnum == bnr
&& qfp->qf_next->qf_lnum < lnum) { && qf_entry_before_pos(qfp->qf_next, pos, linewise)) {
qfp = qfp->qf_next; qfp = qfp->qf_next;
(*errornr)++; (*errornr)++;
} }
if (qfp->qf_lnum >= lnum) { // entry is after 'lnum' if (qf_entry_on_or_after_pos(qfp, pos, linewise)) {
return NULL; return NULL;
} }
if (linewise) {
// If multiple entries are on the same line, then use the first entry // If multiple entries are on the same line, then use the first entry
qfp = qf_find_first_entry_on_line(qfp, errornr); qfp = qf_find_first_entry_on_line(qfp, errornr);
}
return qfp; return qfp;
} }
/// Find a quickfix entry in 'qfl' closest to line 'lnum' in buffer 'bnr' in /// Find a quickfix entry in 'qfl' closest to position 'pos' in buffer 'bnr' in
/// the direction 'dir'. /// the direction 'dir'.
static qfline_T *qf_find_closest_entry(qf_list_T *qfl, static qfline_T *qf_find_closest_entry(
qf_list_T *qfl,
int bnr, int bnr,
linenr_T lnum, const pos_T *pos,
int dir, Direction dir,
int *errornr) bool linewise,
int *errornr
)
FUNC_ATTR_NONNULL_ALL
{ {
qfline_T *qfp; qfline_T *qfp;
@ -4653,33 +4729,38 @@ static qfline_T *qf_find_closest_entry(qf_list_T *qfl,
} }
if (dir == FORWARD) { if (dir == FORWARD) {
qfp = qf_find_entry_on_next_line(bnr, lnum, qfp, errornr); qfp = qf_find_entry_after_pos(bnr, pos, linewise, qfp, errornr);
} else { } else {
qfp = qf_find_entry_on_prev_line(bnr, lnum, qfp, errornr); qfp = qf_find_entry_before_pos(bnr, pos, linewise, qfp, errornr);
} }
return qfp; return qfp;
} }
/// Get the nth quickfix entry below the specified entry treating multiple /// Get the nth quickfix entry below the specified entry. Searches forward in
/// entries on a single line as one. Searches forward in the list. /// the list. If linewise is true, then treat multiple entries on a single line
static void qf_get_nth_below_entry(qfline_T *entry, /// as one.
int *errornr, static void qf_get_nth_below_entry(qfline_T *entry, linenr_T n,
linenr_T n) bool linewise, int *errornr)
FUNC_ATTR_NONNULL_ALL
{ {
while (n-- > 0 && !got_int) { while (n-- > 0 && !got_int) {
// qfline_T *first_entry = entry; // qfline_T *first_entry = entry;
int first_errornr = *errornr; int first_errornr = *errornr;
if (linewise) {
// Treat all the entries on the same line in this file as one // Treat all the entries on the same line in this file as one
entry = qf_find_last_entry_on_line(entry, errornr); entry = qf_find_last_entry_on_line(entry, errornr);
}
if (entry->qf_next == NULL if (entry->qf_next == NULL
|| entry->qf_next->qf_fnum != entry->qf_fnum) { || entry->qf_next->qf_fnum != entry->qf_fnum) {
if (linewise) {
// If multiple entries are on the same line, then use the first // If multiple entries are on the same line, then use the first
// entry // entry
// entry = first_entry; // entry = first_entry;
*errornr = first_errornr; *errornr = first_errornr;
}
break; break;
} }
@ -4688,11 +4769,12 @@ static void qf_get_nth_below_entry(qfline_T *entry,
} }
} }
/// Get the nth quickfix entry above the specified entry treating multiple /// Get the nth quickfix entry above the specified entry. Searches backwards in
/// entries on a single line as one. Searches backwards in the list. /// the list. If linewise is TRUE, then treat multiple entries on a single line
static void qf_get_nth_above_entry(qfline_T *entry, /// as one.
int *errornr, static void qf_get_nth_above_entry(qfline_T *entry, linenr_T n,
linenr_T n) bool linewise, int *errornr)
FUNC_ATTR_NONNULL_ALL
{ {
while (n-- > 0 && !got_int) { while (n-- > 0 && !got_int) {
if (entry->qf_prev == NULL if (entry->qf_prev == NULL
@ -4703,25 +4785,30 @@ static void qf_get_nth_above_entry(qfline_T *entry,
entry = entry->qf_prev; entry = entry->qf_prev;
(*errornr)--; (*errornr)--;
// If multiple entries are on the same line, then use the first entry if (linewise) {
entry = qf_find_first_entry_on_line(entry, errornr); entry = qf_find_first_entry_on_line(entry, errornr);
} }
} }
}
/// Find the n'th quickfix entry adjacent to line 'lnum' in buffer 'bnr' in the /// Find the n'th quickfix entry adjacent to position 'pos' in buffer 'bnr' in
/// specified direction. /// the specified direction. Returns the error number in the quickfix list or 0
/// Returns the error number in the quickfix list or 0 if an entry is not found. /// if an entry is not found.
static int qf_find_nth_adj_entry(qf_list_T *qfl, static int qf_find_nth_adj_entry(
qf_list_T *qfl,
int bnr, int bnr,
linenr_T lnum, pos_T *pos,
linenr_T n, linenr_T n,
int dir) Direction dir,
bool linewise
)
FUNC_ATTR_NONNULL_ALL
{ {
qfline_T *adj_entry;
int errornr; int errornr;
// Find an entry closest to the specified line // Find an entry closest to the specified position
adj_entry = qf_find_closest_entry(qfl, bnr, lnum, dir, &errornr); qfline_T *const adj_entry = qf_find_closest_entry(qfl, bnr, pos, dir,
linewise, &errornr);
if (adj_entry == NULL) { if (adj_entry == NULL) {
return 0; return 0;
} }
@ -4729,24 +4816,25 @@ static int qf_find_nth_adj_entry(qf_list_T *qfl,
if (--n > 0) { if (--n > 0) {
// Go to the n'th entry in the current buffer // Go to the n'th entry in the current buffer
if (dir == FORWARD) { if (dir == FORWARD) {
qf_get_nth_below_entry(adj_entry, &errornr, n); qf_get_nth_below_entry(adj_entry, n, linewise, &errornr);
} else { } else {
qf_get_nth_above_entry(adj_entry, &errornr, n); qf_get_nth_above_entry(adj_entry, n, linewise, &errornr);
} }
} }
return errornr; return errornr;
} }
/// Jump to a quickfix entry in the current file nearest to the current line. /// Jump to a quickfix entry in the current file nearest to the current line or
/// ":cabove", ":cbelow", ":labove" and ":lbelow" commands /// current line/col.
/// ":cabove", ":cbelow", ":labove", ":lbelow", ":cafter", ":cbefore",
/// ":lafter" and ":lbefore" commands
void ex_cbelow(exarg_T *eap) void ex_cbelow(exarg_T *eap)
{ {
qf_info_T *qi; qf_info_T *qi;
qf_list_T *qfl; qf_list_T *qfl;
int dir; int dir;
int buf_has_flag; int buf_has_flag;
int errornr = 0;
if (eap->addr_count > 0 && eap->line2 <= 0) { if (eap->addr_count > 0 && eap->line2 <= 0) {
EMSG(_(e_invrange)); EMSG(_(e_invrange));
@ -4754,7 +4842,8 @@ void ex_cbelow(exarg_T *eap)
} }
// Check whether the current buffer has any quickfix entries // Check whether the current buffer has any quickfix entries
if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow) { if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow
|| eap->cmdidx == CMD_cbefore || eap->cmdidx == CMD_cafter) {
buf_has_flag = BUF_HAS_QF_ENTRY; buf_has_flag = BUF_HAS_QF_ENTRY;
} else { } else {
buf_has_flag = BUF_HAS_LL_ENTRY; buf_has_flag = BUF_HAS_LL_ENTRY;
@ -4775,14 +4864,30 @@ void ex_cbelow(exarg_T *eap)
return; return;
} }
if (eap->cmdidx == CMD_cbelow || eap->cmdidx == CMD_lbelow) { if (eap->cmdidx == CMD_cbelow
|| eap->cmdidx == CMD_lbelow
|| eap->cmdidx == CMD_cafter
|| eap->cmdidx == CMD_lafter) {
// Forward motion commands
dir = FORWARD; dir = FORWARD;
} else { } else {
dir = BACKWARD; dir = BACKWARD;
} }
errornr = qf_find_nth_adj_entry(qfl, curbuf->b_fnum, curwin->w_cursor.lnum, pos_T pos = curwin->w_cursor;
eap->addr_count > 0 ? eap->line2 : 0, dir); // A quickfix entry column number is 1 based whereas cursor column
// number is 0 based. Adjust the column number.
pos.col++;
const int errornr = qf_find_nth_adj_entry(
qfl,
curbuf->b_fnum,
&pos,
eap->addr_count > 0 ? eap->line2 : 0,
dir,
eap->cmdidx == CMD_cbelow
|| eap->cmdidx == CMD_lbelow
|| eap->cmdidx == CMD_cabove
|| eap->cmdidx == CMD_labove);
if (errornr > 0) { if (errornr > 0) {
qf_jump(qi, 0, errornr, false); qf_jump(qi, 0, errornr, false);

View File

@ -40,6 +40,8 @@ func s:setup_commands(cchar)
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 Xbelow <mods><count>cbelow
command! -count=1 -nargs=0 Xabove <mods><count>cabove command! -count=1 -nargs=0 Xabove <mods><count>cabove
command! -count=1 -nargs=0 Xbefore <mods><count>cbefore
command! -count=1 -nargs=0 Xafter <mods><count>cafter
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')
@ -75,6 +77,8 @@ func s:setup_commands(cchar)
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 Xbelow <mods><count>lbelow
command! -count=1 -nargs=0 Xabove <mods><count>labove command! -count=1 -nargs=0 Xabove <mods><count>labove
command! -count=1 -nargs=0 Xbefore <mods><count>lbefore
command! -count=1 -nargs=0 Xafter <mods><count>lafter
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')
@ -4201,17 +4205,22 @@ func Test_empty_qfbuf()
endfunc endfunc
" Test for the :cbelow, :cabove, :lbelow and :labove commands. " Test for the :cbelow, :cabove, :lbelow and :labove commands.
" And for the :cafter, :cbefore, :lafter and :lbefore commands.
func Xtest_below(cchar) func Xtest_below(cchar)
call s:setup_commands(a:cchar) call s:setup_commands(a:cchar)
" No quickfix/location list " No quickfix/location list
call assert_fails('Xbelow', 'E42:') call assert_fails('Xbelow', 'E42:')
call assert_fails('Xabove', 'E42:') call assert_fails('Xabove', 'E42:')
call assert_fails('Xbefore', 'E42:')
call assert_fails('Xafter', 'E42:')
" Empty quickfix/location list " Empty quickfix/location list
call g:Xsetlist([]) call g:Xsetlist([])
call assert_fails('Xbelow', 'E42:') call assert_fails('Xbelow', 'E42:')
call assert_fails('Xabove', 'E42:') call assert_fails('Xabove', 'E42:')
call assert_fails('Xbefore', 'E42:')
call assert_fails('Xafter', 'E42:')
call s:create_test_file('X1') call s:create_test_file('X1')
call s:create_test_file('X2') call s:create_test_file('X2')
@ -4225,39 +4234,74 @@ func Xtest_below(cchar)
call assert_fails('Xabove', 'E42:') call assert_fails('Xabove', 'E42:')
call assert_fails('3Xbelow', 'E42:') call assert_fails('3Xbelow', 'E42:')
call assert_fails('4Xabove', 'E42:') call assert_fails('4Xabove', 'E42:')
call assert_fails('Xbefore', 'E42:')
call assert_fails('Xafter', 'E42:')
call assert_fails('3Xbefore', 'E42:')
call assert_fails('4Xafter', 'E42:')
" Test the commands with various arguments " Test the commands with various arguments
Xexpr ["X1:5:L5", "X2:5:L5", "X2:10:L10", "X2:15:L15", "X3:3:L3"] Xexpr ["X1:5:3:L5", "X2:5:2:L5", "X2:10:3:L10", "X2:15:4:L15", "X3:3:5:L3"]
edit +7 X2 edit +7 X2
Xabove Xabove
call assert_equal(['X2', 5], [bufname(''), line('.')]) call assert_equal(['X2', 5], [bufname(''), line('.')])
call assert_fails('Xabove', 'E553:') call assert_fails('Xabove', 'E553:')
normal 7G
Xbefore
call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
call assert_fails('Xbefore', 'E553:')
normal 2j normal 2j
Xbelow Xbelow
call assert_equal(['X2', 10], [bufname(''), line('.')]) call assert_equal(['X2', 10], [bufname(''), line('.')])
normal 7G
Xafter
call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
" Last error in this file " Last error in this file
Xbelow 99 Xbelow 99
call assert_equal(['X2', 15], [bufname(''), line('.')]) call assert_equal(['X2', 15], [bufname(''), line('.')])
call assert_fails('Xbelow', 'E553:') call assert_fails('Xbelow', 'E553:')
normal gg
Xafter 99
call assert_equal(['X2', 15, 4], [bufname(''), line('.'), col('.')])
call assert_fails('Xafter', 'E553:')
" First error in this file " First error in this file
Xabove 99 Xabove 99
call assert_equal(['X2', 5], [bufname(''), line('.')]) call assert_equal(['X2', 5], [bufname(''), line('.')])
call assert_fails('Xabove', 'E553:') call assert_fails('Xabove', 'E553:')
normal G
Xbefore 99
call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
call assert_fails('Xbefore', 'E553:')
normal gg normal gg
Xbelow 2 Xbelow 2
call assert_equal(['X2', 10], [bufname(''), line('.')]) call assert_equal(['X2', 10], [bufname(''), line('.')])
normal gg
Xafter 2
call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
normal G normal G
Xabove 2 Xabove 2
call assert_equal(['X2', 10], [bufname(''), line('.')]) call assert_equal(['X2', 10], [bufname(''), line('.')])
normal G
Xbefore 2
call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
edit X4 edit X4
call assert_fails('Xabove', 'E42:') call assert_fails('Xabove', 'E42:')
call assert_fails('Xbelow', 'E42:') call assert_fails('Xbelow', 'E42:')
call assert_fails('Xbefore', 'E42:')
call assert_fails('Xafter', 'E42:')
if a:cchar == 'l' if a:cchar == 'l'
" If a buffer has location list entries from some other window but not " If a buffer has location list entries from some other window but not
" from the current window, then the commands should fail. " from the current window, then the commands should fail.
edit X1 | split | call setloclist(0, [], 'f') edit X1 | split | call setloclist(0, [], 'f')
call assert_fails('Xabove', 'E776:') call assert_fails('Xabove', 'E776:')
call assert_fails('Xbelow', 'E776:') call assert_fails('Xbelow', 'E776:')
call assert_fails('Xbefore', 'E776:')
call assert_fails('Xafter', 'E776:')
close close
endif endif
@ -4268,27 +4312,52 @@ func Xtest_below(cchar)
edit +1 X2 edit +1 X2
Xbelow 2 Xbelow 2
call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')]) call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')])
normal 1G
Xafter 2
call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
normal gg normal gg
Xbelow 99 Xbelow 99
call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')]) call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')])
normal gg
Xafter 99
call assert_equal(['X2', 15, 3], [bufname(''), line('.'), col('.')])
normal G normal G
Xabove 2 Xabove 2
call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')]) call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')])
normal G
Xbefore 2
call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')])
normal G normal G
Xabove 99 Xabove 99
call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')]) call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
normal G
Xbefore 99
call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
normal 10G normal 10G
Xabove Xabove
call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')]) call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
normal 10G$
2Xbefore
call assert_equal(['X2', 10, 2], [bufname(''), line('.'), col('.')])
normal 10G normal 10G
Xbelow Xbelow
call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')]) call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')])
normal 9G
5Xafter
call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')])
" Invalid range " Invalid range
if a:cchar == 'c' if a:cchar == 'c'
call assert_fails('-2cbelow', 'E16:') call assert_fails('-2cbelow', 'E16:')
call assert_fails('-2cafter', 'E16:')
else else
call assert_fails('-2lbelow', 'E16:') call assert_fails('-2lbelow', 'E16:')
call assert_fails('-2lafter', 'E16:')
endif endif
call delete('X1') call delete('X1')