Patch 8.1.1256
Problem:    Cannot navigate through errors relative to the cursor.
Solution:   Add :cabove, :cbelow, :labove and :lbelow. (Yegappan Lakshmanan,
            closes #4316)
Files:      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


*** ../vim-8.1.1255/runtime/doc/index.txt       2019-04-27 20:36:52.526303597 
+0200
--- runtime/doc/index.txt       2019-05-03 21:35:09.162087906 +0200
***************
*** 1140,1150 ****
--- 1192,1204 ----
  |: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
***************
*** 1302,1313 ****
--- 1356,1369 ----
  |: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
--- 1703,1708 ----
*** ../vim-8.1.1255/runtime/doc/quickfix.txt    2019-03-17 16:39:01.566006172 
+0100
--- runtime/doc/quickfix.txt    2019-05-03 21:35:09.166087887 +0200
***************
*** 123,128 ****
--- 123,158 ----
                        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
*** ../vim-8.1.1255/src/ex_cmdidxs.h    2019-04-27 20:36:52.530303581 +0200
--- src/ex_cmdidxs.h    2019-05-03 21:42:06.720008024 +0200
***************
*** 8,36 ****
    /* 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
  };
  
  /*
--- 8,36 ----
    /* a */ 0,
    /* b */ 19,
    /* c */ 42,
!   /* 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,49 ****
  { /*         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 },
    /* 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 },
--- 43,49 ----
  { /*         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, 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,58 ****
    /* 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 },
    /* 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 },
--- 52,58 ----
    /* 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, 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,72 ****
    /* 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;
--- 69,72 ----
    /* 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 = 554;
*** ../vim-8.1.1255/src/ex_cmds.h       2019-05-01 18:08:38.263237252 +0200
--- src/ex_cmds.h       2019-05-03 21:43:59.707432410 +0200
***************
*** 252,257 ****
--- 252,260 ----
  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,275 ****
--- 273,281 ----
  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,731 ****
--- 732,740 ----
  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,749 ****
--- 753,761 ----
  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),
*** ../vim-8.1.1255/src/ex_docmd.c      2019-05-01 21:43:39.076257243 +0200
--- src/ex_docmd.c      2019-05-03 21:35:09.166087887 +0200
***************
*** 56,61 ****
--- 56,62 ----
  # 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
*** ../vim-8.1.1255/src/proto/quickfix.pro      2019-02-05 21:23:00.600559169 
+0100
--- src/proto/quickfix.pro      2019-05-03 21:35:09.166087887 +0200
***************
*** 23,28 ****
--- 23,29 ----
  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);
*** ../vim-8.1.1255/src/quickfix.c      2019-05-02 20:17:29.318924421 +0200
--- src/quickfix.c      2019-05-03 21:35:09.166087887 +0200
***************
*** 177,182 ****
--- 177,183 ----
  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)
***************
*** 1494,1499 ****
--- 1495,1510 ----
  }
  
  /*
+  * 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
   */
      static qf_list_T *
***************
*** 2700,2706 ****
      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--)
--- 2711,2716 ----
***************
*** 4886,4892 ****
      qfp = qfl->qf_start;
  
      // check if the list has valid errors
!     if (qfl->qf_count <= 0 || qfl->qf_nonevalid)
        return 1;
  
      for (i = 1; i <= qfl->qf_index && qfp!= NULL; i++, qfp = qfp->qf_next)
--- 4896,4902 ----
      qfp = qfl->qf_start;
  
      // check if the list has valid errors
!     if (!qf_list_has_valid_entries(qfl))
        return 1;
  
      for (i = 1; i <= qfl->qf_index && qfp!= NULL; i++, qfp = qfp->qf_next)
***************
*** 4924,4930 ****
      int               prev_fnum = 0;
  
      // check if the list has valid errors
!     if (qfl->qf_count <= 0 || qfl->qf_nonevalid)
        return 1;
  
      eidx = 0;
--- 4934,4940 ----
      int               prev_fnum = 0;
  
      // check if the list has valid errors
!     if (!qf_list_has_valid_entries(qfl))
        return 1;
  
      eidx = 0;
***************
*** 5045,5050 ****
--- 5055,5355 ----
  }
  
  /*
+  * 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
   */
      static char_u *
*** ../vim-8.1.1255/src/testdir/test_quickfix.vim       2019-05-02 
20:17:29.322924398 +0200
--- src/testdir/test_quickfix.vim       2019-05-03 21:48:52.249927300 +0200
***************
*** 37,42 ****
--- 37,44 ----
      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,75 ****
--- 72,79 ----
      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,4037 ****
--- 4039,4147 ----
    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
*** ../vim-8.1.1255/src/version.c       2019-05-03 21:19:58.926404208 +0200
--- src/version.c       2019-05-03 21:35:46.089908080 +0200
***************
*** 769,770 ****
--- 769,772 ----
  {   /* Add new patch number below this line */
+ /**/
+     1256,
  /**/

-- 
A hamburger walks into a bar, and the bartender says: "I'm sorry,
but we don't serve food here."

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui