]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.1.1256: cannot navigate through errors relative to the cursor v8.1.1256
authorBram Moolenaar <Bram@vim.org>
Fri, 3 May 2019 19:56:35 +0000 (21:56 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 3 May 2019 19:56:35 +0000 (21:56 +0200)
Problem:    Cannot navigate through errors relative to the cursor.
Solution:   Add :cabove, :cbelow, :labove and :lbelow. (Yegappan Lakshmanan,
            closes #4316)

runtime/doc/index.txt
runtime/doc/quickfix.txt
src/ex_cmdidxs.h
src/ex_cmds.h
src/ex_docmd.c
src/proto/quickfix.pro
src/quickfix.c
src/testdir/test_quickfix.vim
src/version.c

index c95de5b7bb71e5a1fef814784c0b038e6eb005be..7192905561329d259a415c5bb83cce825d4aa939 100644 (file)
@@ -1188,11 +1188,13 @@ tag             command         action ~
 |:cNfile|      :cNf[ile]       go to last error in previous file
 |:cabbrev|     :ca[bbrev]      like ":abbreviate" but 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
 |:caddexpr|    :cadde[xpr]     add errors from expr
 |:caddfile|    :caddf[ile]     add error message to current quickfix list
 |:call|                :cal[l]         call a function
 |: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
 |:cbuffer|     :cb[uffer]      parse error messages and jump to first error
 |:cc|          :cc             go to specific error
@@ -1350,12 +1352,14 @@ tag             command         action ~
 |:lNext|       :lN[ext]        go to previous entry in location list
 |:lNfile|      :lNf[ile]       go to last entry in previous file
 |:list|                :l[ist]         print lines
+|:labove|      :lab[ove]       go to location above current line
 |:laddexpr|    :lad[dexpr]     add locations from expr
 |:laddbuffer|  :laddb[uffer]   add locations from buffer
 |:laddfile|    :laddf[ile]     add locations to current location list
 |:last|                :la[st]         go to the last file in the argument list
 |:language|    :lan[guage]     set the language (locale)
 |: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
 |:lbuffer|     :lb[uffer]      parse locations and jump to first location
 |:lcd|         :lc[d]          change directory locally
index 0ceb231b84487d7b6133eca56c2907da2ece6d40..6f17b77d485a5683b0324a3bf77e2fc9b2206ade 100644 (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
                        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*
 :[count]cnf[ile][!]    Display the first error in the [count] next file in
                        the list that includes a file name.  If there are no
index 1531e68c1ba57d07031e81831f3d7634a4588b98..c63aa46f5a7bdf9eb77de64dadc72b8b1e73c4e0 100644 (file)
@@ -8,29 +8,29 @@ static const unsigned short cmdidxs1[26] =
   /* a */ 0,
   /* b */ 19,
   /* c */ 42,
-  /* d */ 103,
-  /* e */ 125,
-  /* f */ 145,
-  /* g */ 161,
-  /* h */ 167,
-  /* i */ 176,
-  /* j */ 194,
-  /* k */ 196,
-  /* l */ 201,
-  /* m */ 259,
-  /* n */ 277,
-  /* o */ 297,
-  /* p */ 309,
-  /* q */ 348,
-  /* r */ 351,
-  /* s */ 371,
-  /* t */ 439,
-  /* u */ 484,
-  /* v */ 495,
-  /* w */ 513,
-  /* x */ 527,
-  /* y */ 536,
-  /* z */ 537
+  /* d */ 105,
+  /* e */ 127,
+  /* f */ 147,
+  /* g */ 163,
+  /* h */ 169,
+  /* i */ 178,
+  /* j */ 196,
+  /* k */ 198,
+  /* l */ 203,
+  /* m */ 263,
+  /* n */ 281,
+  /* o */ 301,
+  /* p */ 313,
+  /* q */ 352,
+  /* r */ 355,
+  /* s */ 375,
+  /* t */ 443,
+  /* u */ 488,
+  /* v */ 499,
+  /* w */ 517,
+  /* x */ 531,
+  /* y */ 540,
+  /* 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 */ {  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 },
-  /* 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 },
   /* 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 },
@@ -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 },
   /* 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 },
-  /* 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 },
   /* 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 },
@@ -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 }
 };
 
-static const int command_count = 550;
+static const int command_count = 554;
index 63196afd320d449d6d7251d0065e18f22bde749e..73914725a2030c128c3e8d99e8a748ee331137c0 100644 (file)
@@ -252,6 +252,9 @@ EX(CMD_cabbrev,             "cabbrev",      ex_abbreviate,
 EX(CMD_cabclear,       "cabclear",     ex_abclear,
                        EXTRA|TRLBAR|CMDWIN,
                        ADDR_NONE),
+EX(CMD_cabove,         "cabove",       ex_cbelow,
+                       RANGE|TRLBAR,
+                       ADDR_OTHER),
 EX(CMD_caddbuffer,     "caddbuffer",   ex_cbuffer,
                        RANGE|WORD1|TRLBAR,
                        ADDR_OTHER),
@@ -270,6 +273,9 @@ EX(CMD_catch,               "catch",        ex_catch,
 EX(CMD_cbuffer,                "cbuffer",      ex_cbuffer,
                        BANG|RANGE|WORD1|TRLBAR,
                        ADDR_OTHER),
+EX(CMD_cbelow,         "cbelow",       ex_cbelow,
+                       RANGE|TRLBAR,
+                       ADDR_OTHER),
 EX(CMD_cbottom,                "cbottom",      ex_cbottom,
                        TRLBAR,
                        ADDR_NONE),
@@ -726,6 +732,9 @@ EX(CMD_lNfile,              "lNfile",       ex_cnext,
 EX(CMD_last,           "last",         ex_last,
                        EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR,
                        ADDR_NONE),
+EX(CMD_labove,         "labove",       ex_cbelow,
+                       RANGE|TRLBAR,
+                       ADDR_OTHER),
 EX(CMD_language,       "language",     ex_language,
                        EXTRA|TRLBAR|CMDWIN,
                        ADDR_NONE),
@@ -744,6 +753,9 @@ EX(CMD_later,               "later",        ex_later,
 EX(CMD_lbuffer,                "lbuffer",      ex_cbuffer,
                        BANG|RANGE|WORD1|TRLBAR,
                        ADDR_OTHER),
+EX(CMD_lbelow,         "lbelow",       ex_cbelow,
+                       RANGE|TRLBAR,
+                       ADDR_OTHER),
 EX(CMD_lbottom,                "lbottom",      ex_cbottom,
                        TRLBAR,
                        ADDR_NONE),
index b317f53bfc877c4034c69577ccda4d87e2f5e5a7..db9bd0698a364166dc07c546cf593bebf5e07b24 100644 (file)
@@ -56,6 +56,7 @@ static int    getargopt(exarg_T *eap);
 # define ex_cbuffer            ex_ni
 # define ex_cc                 ex_ni
 # define ex_cnext              ex_ni
+# define ex_cbelow             ex_ni
 # define ex_cfile              ex_ni
 # define qf_list               ex_ni
 # define qf_age                        ex_ni
index c8d5956fb517033d9eff502d1d9580664825635e..1ae339e2fb67fda34d71ddc36c251a515a7f6785 100644 (file)
@@ -23,6 +23,7 @@ int qf_get_cur_idx(exarg_T *eap);
 int qf_get_cur_valid_idx(exarg_T *eap);
 void ex_cc(exarg_T *eap);
 void ex_cnext(exarg_T *eap);
+void ex_cbelow(exarg_T *eap);
 void ex_cfile(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);
index f90934387938e90498c5a063cf71908d498a6c09..e8c7829616ab2d130b71dda7c8dd7b838fa658c1 100644 (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    unload_dummy_buffer(buf_T *buf, char_u *dirname_start);
 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
 #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;
 }
 
+/*
+ * 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
  */
@@ -2700,7 +2711,6 @@ get_nth_valid_entry(
     int                        qf_idx = qfl->qf_index;
     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--)
@@ -4886,7 +4896,7 @@ qf_get_cur_valid_idx(exarg_T *eap)
     qfp = qfl->qf_start;
 
     // check if the list has valid errors
-    if (qfl->qf_count <= 0 || qfl->qf_nonevalid)
+    if (!qf_list_has_valid_entries(qfl))
        return 1;
 
     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;
 
     // check if the list has valid errors
-    if (qfl->qf_count <= 0 || qfl->qf_nonevalid)
+    if (!qf_list_has_valid_entries(qfl))
        return 1;
 
     eidx = 0;
@@ -5044,6 +5054,301 @@ 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, 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
  */
index ccd7778e42151e7c1af50d637bd1350b498231dc..ee98502a79b643fd9f60b7bf2b745aa58692e115 100644 (file)
@@ -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')
@@ -4035,3 +4039,109 @@ func Test_empty_qfbuf()
   enew
   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
index f03346c557bd8ee96fbadd354cec07cb9f014aa3..35ef1ae7cc9159f2c99e80db622c5ddc6b37458e 100644 (file)
@@ -767,6 +767,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1256,
 /**/
     1255,
 /**/